diff --git a/.github/workflows/sort-contributors-go.yml b/.github/workflows/sort-contributors-go.yml new file mode 100644 index 000000000..365e11ef1 --- /dev/null +++ b/.github/workflows/sort-contributors-go.yml @@ -0,0 +1,56 @@ +name: Sort Contributors Go Checks + +on: + push: + branches: + - master + paths: + - "scripts/sort_contibutors/**" + - ".github/workflows/sort-contributors-go.yml" + pull_request: + branches: [master] + paths: + - "scripts/sort_contibutors/**" + - ".github/workflows/sort-contributors-go.yml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + go-checks: + name: build test lint security + runs-on: ubuntu-latest + defaults: + run: + working-directory: scripts/sort_contibutors + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: scripts/sort_contibutors/go.mod + cache-dependency-path: scripts/sort_contibutors/go.mod + + - name: Build + run: make build + + - name: Test + run: make test + + - name: Vet + run: make vet + + - name: Install golangci-lint + run: | + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + + - name: Lint + run: make lint + + - name: Security + run: make security diff --git a/README.md b/README.md index 849bafe60..5cacf45df 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,7 @@ Contributors: - [Adarsh A @adarsh-a-tw](https://www.github.com/adarsh-a-tw) - [Diamond Rivero @diamant3](https://www.github.com/diamant3) - [Norbert Wolniak @nwolniak](https://www.github.com/nwolniak) +- [Robert Felber @rfelber](https://www.github.com/rfelber) - [Filip Chyla @fchyla](https://www.github.com/fchyla) - [Dmitry Litosh @Dlitosh](https://www.github.com/Dlitosh) - [Vineeth Jagadeesh @djvinnie](https://www.github.com/djvinnie) @@ -429,6 +430,8 @@ Contributors: - [Madhu Akula @madhuakula](https://www.github.com/madhuakula) - [Mike Woudenberg @mikewoudenberg](https://www.github.com/mikewoudenberg) - [Spyros @northdpole](https://www.github.com/northdpole) +- [Seershan Mitra @seershan](https://www.github.com/seershan) +- [moeedrehman135 @moeedrehman135](https://www.github.com/moeedrehman135) - [RubenAtBinx @RubenAtBinx](https://www.github.com/RubenAtBinx) - [Alex Bender @alex-bender](https://www.github.com/alex-bender) - [Danny Lloyd @dannylloyd](https://www.github.com/dannylloyd) diff --git a/renovate.json b/renovate.json index 2d225e022..ac13d64ad 100644 --- a/renovate.json +++ b/renovate.json @@ -94,6 +94,16 @@ ], "enabled": true }, + { + "groupName": "sort-contibutors-go", + "matchManagers": [ + "gomod" + ], + "matchFileNames": [ + "scripts/sort_contibutors/go.mod" + ], + "enabled": true + }, { "groupName": "pre-commit", "matchFileNames": [ diff --git a/scripts/sort_contibutors/Makefile b/scripts/sort_contibutors/Makefile new file mode 100644 index 000000000..fb91374f9 --- /dev/null +++ b/scripts/sort_contibutors/Makefile @@ -0,0 +1,37 @@ +BINARY_NAME := sort-contributors +BUILD_DIR := bin +GO := go + +.PHONY: build run lint test vet security clean + +build: + mkdir -p $(BUILD_DIR) + $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) . + +run: build + ./$(BUILD_DIR)/$(BINARY_NAME) $(ARGS) + +lint: + @command -v golangci-lint >/dev/null 2>&1 || { \ + echo "golangci-lint not found. Install from https://golangci-lint.run/usage/install/"; \ + exit 1; \ + } + golangci-lint run ./... + +test: + $(GO) test ./... + +vet: + $(GO) vet ./... + +security: + @command -v gosec >/dev/null 2>&1 || { \ + echo "gosec not found. Installing with: go install github.com/securego/gosec/v2/cmd/gosec@latest"; \ + GOBIN=$$(pwd)/.tools $(GO) install github.com/securego/gosec/v2/cmd/gosec@latest || exit 1; \ + PATH=$$(pwd)/.tools:$$PATH gosec ./...; \ + exit $$?; \ + } + gosec ./... + +clean: + rm -rf $(BUILD_DIR) .tools diff --git a/scripts/sort_contibutors/Pipfile b/scripts/sort_contibutors/Pipfile deleted file mode 100644 index 0757494bb..000000000 --- a/scripts/sort_contibutors/Pipfile +++ /dev/null @@ -1,11 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] - -[dev-packages] - -[requires] -python_version = "3.11" diff --git a/scripts/sort_contibutors/Pipfile.lock b/scripts/sort_contibutors/Pipfile.lock deleted file mode 100644 index 8b082b79e..000000000 --- a/scripts/sort_contibutors/Pipfile.lock +++ /dev/null @@ -1,189 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "ed6d5d614626ae28e274e453164affb26694755170ccab3aa5866f093d51d3e4" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.11" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "certifi": { - "hashes": [ - "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", - "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7" - ], - "markers": "python_version >= '3.7'", - "version": "==2026.2.25" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", - "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", - "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", - "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", - "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", - "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", - "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", - "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", - "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", - "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8", - "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264", - "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", - "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", - "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", - "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", - "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", - "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa", - "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", - "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", - "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297", - "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", - "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e", - "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", - "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8", - "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", - "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", - "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", - "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", - "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", - "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", - "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7", - "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", - "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b", - "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", - "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687", - "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9", - "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14", - "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", - "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", - "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", - "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", - "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a", - "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", - "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", - "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", - "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", - "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", - "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", - "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", - "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532", - "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", - "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae", - "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", - "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64", - "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", - "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", - "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", - "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", - "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", - "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", - "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", - "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", - "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597", - "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", - "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", - "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", - "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54", - "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", - "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", - "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4", - "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", - "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", - "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", - "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", - "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", - "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", - "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", - "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", - "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", - "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", - "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", - "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", - "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", - "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", - "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", - "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", - "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc", - "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", - "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", - "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", - "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", - "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", - "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", - "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", - "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237", - "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", - "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778", - "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", - "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", - "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", - "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", - "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f", - "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5", - "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611", - "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", - "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", - "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", - "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", - "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", - "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e", - "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", - "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", - "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", - "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", - "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", - "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe", - "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", - "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17", - "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833", - "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", - "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", - "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", - "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2", - "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", - "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982", - "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", - "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", - "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104", - "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659" - ], - "markers": "python_version >= '3.7'", - "version": "==3.4.6" - }, - "idna": { - "hashes": [ - "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", - "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" - ], - "markers": "python_version >= '3.8'", - "version": "==3.11" - }, - "requests": { - "hashes": [ - "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", - "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652" - ], - "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==2.33.0" - }, - "urllib3": { - "hashes": [ - "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", - "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" - ], - "markers": "python_version >= '3.9'", - "version": "==2.6.3" - } - }, - "develop": {} -} diff --git a/scripts/sort_contibutors/__pycache__/main.cpython-311.pyc b/scripts/sort_contibutors/__pycache__/main.cpython-311.pyc deleted file mode 100644 index c98e76ec9..000000000 Binary files a/scripts/sort_contibutors/__pycache__/main.cpython-311.pyc and /dev/null differ diff --git a/scripts/sort_contibutors/go.mod b/scripts/sort_contibutors/go.mod new file mode 100644 index 000000000..b4493c30a --- /dev/null +++ b/scripts/sort_contibutors/go.mod @@ -0,0 +1,3 @@ +module github.com/owasp/wrongsecrets/scripts/sort_contibutors + +go 1.26 diff --git a/scripts/sort_contibutors/main.go b/scripts/sort_contibutors/main.go new file mode 100644 index 000000000..62e1cb91b --- /dev/null +++ b/scripts/sort_contibutors/main.go @@ -0,0 +1,460 @@ +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "html" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "sort" + "strings" + "time" +) + +const ( + githubAPIBase = "https://api.github.com" + outputHTMLFile = "contributors_file.html" + outputMarkdownFile = "contributors_file.md" +) + +var errInvalidToken = errors.New("invalid token") + +type Contributor struct { + Username string + Name string + Ranking int +} + +type contributorAPI struct { + Login string `json:"login"` + Contributions int `json:"contributions"` +} + +type app struct { + client *http.Client + token string + outputDir string + nameCache map[string]string +} + +var excludedContributors = map[string]struct{}{ + "DerGut": {}, + "bkimminich": {}, + "MichaelEischer": {}, + "rseedorff": {}, + "jonasbg": {}, + "scornelissen85": {}, + "zadjadr": {}, + "stuebingerb": {}, + "sydseter": {}, + "troygerber": {}, + "skandix": {}, + "saymolet": {}, + "adrianeriksen": {}, + "pseudobeard": {}, + "coffemakingtoaster": {}, + "wurstbrot": {}, + "blucas-accela": {}, + "fwijnholds": {}, + "stefan-schaermeli": {}, + "nickmalcolm": {}, + "orangecola": {}, + "commjoen": {}, + "bendehaan": {}, + "benno001": {}, +} + +var knownNames = map[string]string{ + "puneeth072003": "Puneeth Y", + "f3rn0s": "Fern", + "Novice-expert": "Divyanshu Dev", + "neatzsche": "Chris Elbring Jr.", +} + +func main() { + tokenFlag := flag.String("token", "", "GitHub fine-grained token. If omitted, USER_TOKEN or GITHUB_TOKEN env vars are used") + outputDirFlag := flag.String("output-dir", ".", "Directory where contributors_file.html and contributors_file.md will be written") + flag.Parse() + + token, err := resolveToken(*tokenFlag, os.Getenv) + if err != nil { + fmt.Println(err) + flag.Usage() + os.Exit(1) + } + + outputDir, err := resolveOutputDir(*outputDirFlag) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + application := app{ + client: &http.Client{Timeout: 20 * time.Second}, + token: token, + outputDir: outputDir, + nameCache: map[string]string{}, + } + + if err := application.run(); err != nil { + fmt.Printf("[!] %v\n", err) + os.Exit(1) + } +} + +func resolveToken(tokenFlag string, env func(string) string) (string, error) { + if strings.TrimSpace(tokenFlag) != "" { + return tokenFlag, nil + } + + if token := strings.TrimSpace(env("USER_TOKEN")); token != "" { + return token, nil + } + + if token := strings.TrimSpace(env("GITHUB_TOKEN")); token != "" { + return token, nil + } + + return "", errors.New("missing token: provide -token or set USER_TOKEN/GITHUB_TOKEN") +} + +func resolveOutputDir(outputDirFlag string) (string, error) { + outputDir := strings.TrimSpace(outputDirFlag) + if outputDir == "" { + return "", errors.New("missing output directory") + } + + absPath, err := filepath.Abs(outputDir) + if err != nil { + return "", fmt.Errorf("resolve output directory: %w", err) + } + + return absPath, nil +} + +func (a *app) run() error { + if err := os.MkdirAll(a.outputDir, 0o750); err != nil { + return fmt.Errorf("create output directory: %w", err) + } + + createdDate := time.Now().UTC().Format("2006-01-02") + + leaders := []Contributor{ + {Username: "bendehaan", Name: "Ben de Haan"}, + {Username: "commjoen", Name: "Jeroen Willemsen"}, + } + testers := []Contributor{ + {Username: "davevs", Name: "Dave van Stein"}, + {Username: "drnow4u", Name: "Marcin Nowak"}, + {Username: "mchangsp", Name: "Marc Chang Sing Pang"}, + {Username: "djvinnie", Name: "Vineeth Jagadeesh"}, + } + specialThanks := []Contributor{ + {Username: "madhuakula", Name: "Madhu Akula @madhuakula"}, + {Username: "nbaars", Name: "Nanne Baars @nbaars"}, + {Username: "bkimminich", Name: "Bjorn Kimminich"}, + {Username: "devsecops", Name: "Dan Gora"}, + {Username: "saragluna", Name: "Xiaolu Dai"}, + {Username: "jonathanGiles", Name: "Jonathan Giles"}, + } + + topContributors, contributors, err := a.getContributorsList() + if err != nil { + return err + } + + htmlOutputPath := filepath.Join(a.outputDir, outputHTMLFile) + fmt.Printf("[+] Print to HTML file: %s\n", htmlOutputPath) + htmlPayload := renderHTML(createdDate, leaders, topContributors, contributors, testers, specialThanks) + if err := writeFile(htmlOutputPath, htmlPayload); err != nil { + return fmt.Errorf("write html output: %w", err) + } + + markdownOutputPath := filepath.Join(a.outputDir, outputMarkdownFile) + fmt.Printf("[+] Print to MD file: %s\n", markdownOutputPath) + mdPayload := renderCreatedComment(createdDate) + mdPayload += renderMarkdown(leaders, "Leaders") + mdPayload += renderMarkdown(topContributors, "Top contributors") + mdPayload += renderMarkdown(contributors, "Contributors") + mdPayload += renderMarkdown(testers, "Testers") + mdPayload += renderMarkdown(specialThanks, "Special thanks") + if err := writeFile(markdownOutputPath, mdPayload); err != nil { + return fmt.Errorf("write markdown output: %w", err) + } + + fmt.Println("[+] Done") + return nil +} + +func writeFile(path, payload string) error { + return os.WriteFile(path, []byte(payload), 0o600) +} + +func renderMarkdown(users []Contributor, label string) string { + var b strings.Builder + b.WriteString(label) + b.WriteString(":\n\n") + for _, user := range users { + fmt.Fprintf(&b, "- [%s @%s](https://www.github.com/%s)\n", user.Name, user.Username, user.Username) + } + b.WriteString("\n") + return b.String() +} + +func renderCreatedComment(date string) string { + return fmt.Sprintf("\n\n", date) +} + +func renderHTML(createdDate string, leaders, topContributors, contributors, testers, specialThanks []Contributor) string { + var b strings.Builder + b.WriteString(renderCreatedComment(createdDate)) + b.WriteString("
\n") + writeHTMLSection(&b, "OWASP Project Leaders", leaders) + writeHTMLSection(&b, "Top Contributors", topContributors) + writeHTMLSection(&b, "Contributors", contributors) + writeHTMLSection(&b, "Testers", testers) + writeHTMLSection(&b, "Special mentions for helping out", specialThanks) + b.WriteString("\n") + return b.String() +} + +func writeHTMLSection(b *strings.Builder, title string, users []Contributor) { + b.WriteString(title) + b.WriteString(":\n") + b.WriteString("