Merge branch 'main' into longquanzheng-patch-1

This commit is contained in:
Yassine Benaid 2024-09-10 09:24:14 +01:00 committed by GitHub
commit 5f67bee62c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 1545 additions and 1183 deletions

View File

@ -18,5 +18,3 @@ engines:
ratings: ratings:
paths: paths:
- "**.go" - "**.go"
exclude_paths:
- vendor/

View File

@ -1,13 +0,0 @@
---
name: awesome-go.com
about: website-related
title: "[awesome-go.com]: issue title here"
labels: awesome-go.com
assignees: ''
---
<!--
before opening the issue we recommend that you read our contribution guide, there we talk about how you can contribute to awesome-go.
https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md
-->

View File

@ -1,13 +0,0 @@
---
name: awesome-go related topic
about: Create a report to help us improve
title: ''
labels: help wanted
assignees: ''
---
<!--
before opening the issue we recommend that you read our contribution guide, there we talk about how you can contribute to awesome-go.
https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md
-->

27
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Bug Report
description: Report a bug encountered
labels: ["bug", "pending-review"]
body:
- type: markdown
attributes:
value: |
Thank you very much for opening a bug report at awesome-go.
If you have a feature idea or need help, please go to [our Forum](https://github.com/avelino/awesome-go/discussions).
before opening the issue we recommend that you read our [contribution guide](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md), there we talk about how you can contribute to awesome-go.
- type: checkboxes
id: confirm-search
attributes:
label: Search first
description: Please search [existing issues](https://github.com/avelino/awesome-go/issues) and the [awesome-go forum](https://github.com/avelino/awesome-go/discussions) before reporting.
options:
- label: I searched and no similar issues were found
required: true
- type: textarea
id: problem
attributes:
label: What Happened?
description: |
Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner.
validations:
required: true

7
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,7 @@
contact_links:
- name: Feature request
url: https://github.com/avelino/awesome-go/discussions/new?category=ideas
about: Suggest an idea for awesome-go
- name: Questions & Help
url: https://github.com/avelino/awesome-go/discussions/new?category=q-a
about: Ask a question about awesome-go

View File

@ -1,26 +1,40 @@
> Please check if what you want to add to `awesome-go` list meets [quality standards](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards) before sending pull request. Thanks! ## We want to ensure high quality of the packages. Make sure that you've checked the boxes below before sending a pull request.
**Please provide package links to:** - [ ] I have read the [Contribution Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines)
- [ ] I have read the [Maintainers Note](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#maintainers)
- [ ] I have read the [Quality Standards](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards)
- repo link (github.com, gitlab.com, etc):
- pkg.go.dev:
- goreportcard.com:
- coverage service link ([codecov](https://codecov.io/), [coveralls](https://coveralls.io/), etc.):
**Note**: _that new categories can be added only when there are 3 packages or more._
**Make sure that you've checked the boxes below that apply before you submit PR.**
_Not every repository (project) will require every option, but most projects should. Check the Contribution Guidelines for details._ _Not every repository (project) will require every option, but most projects should. Check the Contribution Guidelines for details._
- [ ] The package has been added to the list in alphabetical order.
- [ ] The package has an appropriate description with correct grammar.
- [ ] As far as I know, the package has not been listed here before.
- [ ] The repo documentation has a pkg.go.dev link. - [ ] The repo documentation has a pkg.go.dev link.
- [ ] The repo documentation has a coverage service link. - [ ] The repo documentation has a coverage service link.
- [ ] The repo documentation has a goreportcard link. - [ ] The repo documentation has a goreportcard link.
- [ ] The repo has a version-numbered release and a go.mod file. - [ ] The repo has a version-numbered release and a go.mod file.
- [ ] I have read the [Contribution Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines), [Maintainers Note](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#maintainers) and [Quality Standards](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards).
- [ ] The repo has a continuous integration process that automatically runs tests that must pass before new pull requests are merged. - [ ] The repo has a continuous integration process that automatically runs tests that must pass before new pull requests are merged.
- [ ] The authors of the project do not commit directly to the repo, but rather use pull requests that run the continuous-integration process. - [ ] Continuous integration is used to attempt to catch issues prior to releasing this package to end-users.
Thanks for your PR, you're awesome! :+1: ## Please provide some links to your package to ease the review
- [ ] forge link (github.com, gitlab.com, etc):
- [ ] pkg.go.dev:
- [ ] goreportcard.com:
- [ ] coverage service link ([codecov](https://codecov.io/), [coveralls](https://coveralls.io/), etc.):
## Pull Request content
- [ ] The package has been added to the list in alphabetical order.
- [ ] The package has an appropriate description with correct grammar.
- [ ] As far as I know, the package has not been listed here before.
## Category quality
_Note that new categories can be added only when there are 3 packages or more._
Packages added a long time ago might not meet the current guidelines anymore. It would be very helpful if you could check 3-5 packages above and below your submission to ensure that they also still meet the Quality Standards.
Please delete one of the following lines:
- [ ] The packages around my addition still meet the Quality Standards.
- [ ] I removed the following packages around my addition: (please give a short reason for each removal)
Thanks for your PR, you're awesome! :sunglasses:

View File

@ -0,0 +1,14 @@
name: Issues spammy check
on:
issues:
types: [opened]
jobs:
mark-as-spam:
name: Remove issues with spammy
runs-on: ubuntu-latest
steps:
- name: close issue
uses: balevine/mark-as-spam@v1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,31 @@
name: First comment in new pull request
on:
pull_request_target:
types: [opened]
jobs:
commentCreated:
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
environment: action
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: first comment
uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Thank you for contributing to [awesome-go](https://awesome-go.com/). We will review your contribution as soon as possible.
Make sure you add the links in the body of the pull request that are requested in the [contribution guide](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md):
- repo link
- pkg.go.dev
- goreportcard.com
- coverage
> Your project is under review. It may take a few days to be approved.

View File

@ -13,10 +13,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: golang:latest container: golang:latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Get dependencies - name: Get dependencies
run: go get -v -t -d ./... run: go get -v -t -d ./...
- name: run script - name: run script
run: go test stale_repositories_test.go scripts.go run: go test -v -run ^TestStaleRepository$
env: env:
OAUTH_TOKEN: ${{secrets.OAUTH_TOKEN}} OAUTH_TOKEN: ${{secrets.OAUTH_TOKEN}}

View File

@ -15,15 +15,15 @@ jobs:
environment: netlify environment: netlify
container: golang:latest container: golang:latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Get dependencies - name: Get dependencies
run: go get -v -t -d ./... run: go get -v -t -d ./...
- name: Make awesome-go.com - name: Make awesome-go.com
run: go run make_site.go scripts.go run: go run .
- name: deploy awesome-go.com - name: deploy awesome-go.com
uses: jsmrcaga/action-netlify-deploy@v1.1.0 uses: jsmrcaga/action-netlify-deploy@v1.1.0
with: with:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_DEPLOY_TO_PROD: true NETLIFY_DEPLOY_TO_PROD: true
build_directory: tmpl build_directory: out

View File

@ -15,8 +15,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: golang:latest container: golang:latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Get dependencies - name: Get dependencies
run: go get -v -t -d ./... run: go get -v -t -d ./...
- name: Run tests - name: Run tests
run: go test repo_test.go scripts.go run: go test main_test.go main.go

5
.gitignore vendored
View File

@ -1,6 +1,5 @@
tmpl/index.html out/
awesome-go awesome-go
vendor
# Folders # Folders
.idea .idea
@ -9,5 +8,3 @@ test_stale_repositories_log
*.exe *.exe
# Local Netlify folder # Local Netlify folder
.netlify .netlify
*.html
sitemap.xml

View File

@ -12,7 +12,7 @@ We invite all those who participate in Awesome Go to help us create safe and pos
A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. Communities mirror the societies in which they exist, and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
@ -20,28 +20,28 @@ If you see someone who is making an extra effort to ensure our community is welc
The following behaviors are expected and requested of all community members: The following behaviors are expected and requested of all community members:
* Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
* Exercise consideration and respect in your speech and actions. * Exercise consideration and respect in your speech and actions.
* Attempt collaboration before conflict. * Attempt collaboration before conflict.
* Refrain from demeaning, discriminatory, or harassing behavior and speech. * Refrain from demeaning, discriminatory, or harassing behavior and speech.
* Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone distressed, or violations of this Code of Conduct, even if they seem inconsequential.
* Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
## 4. Unacceptable Behavior ## 4. Unacceptable Behavior
The following behaviors are considered harassment and are unacceptable within our community: The following behaviors are considered harassment and are unacceptable within our community:
* Violence, threats of violence or violent language directed against another person. * Violence, threats of violence or violent language directed against another person.
* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
* Posting or displaying sexually explicit or violent material. * Posting or displaying sexually explicit or violent material.
* Posting or threatening to post other peoples personally identifying information ("doxing"). * Posting or threatening to post other peoples personally identifying information ("doxing").
* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
* Inappropriate photography or recording. * Inappropriate photography or recording.
* Inappropriate physical contact. You should have someones consent before touching them. * Inappropriate physical contact. You should have someones consent before touching them.
* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
* Deliberate intimidation, stalking or following (online or in person). * Deliberate intimidation, stalking or following (online or in person).
* Advocating for, or encouraging, any of the above behavior. * Advocating for, or encouraging, any of the above behavior.
* Sustained disruption of community events, including talks and presentations. * Sustained disruption of community events, including talks and presentations.
## 5. Consequences of Unacceptable Behavior ## 5. Consequences of Unacceptable Behavior
@ -49,11 +49,11 @@ Unacceptable behavior from any community member, including sponsors and those wi
Anyone asked to stop unacceptable behavior is expected to comply immediately. Anyone asked to stop unacceptable behavior is expected to comply immediately.
If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community unexpected (and without refund in the case of a paid event).
## 6. Reporting Guidelines ## 6. Reporting Guidelines
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. t@avelino.xxx. If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible.
[Reporting Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines) [Reporting Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines)
@ -73,7 +73,7 @@ This code of conduct and its related procedures also applies to unacceptable beh
## 9. Contact info ## 9. Contact info
t@avelino.xxx avelinorun AT gmail DOT com
## 10. License and attribution ## 10. License and attribution
@ -81,4 +81,4 @@ This Code of Conduct is distributed under a [Creative Commons Attribution-ShareA
Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) Retrieved on November 22, 2016

View File

@ -1,19 +1,21 @@
This resource was made by the Go community and wouldn't be possible without you! This resource was made by the Go community and wouldn't be possible without you!
We appreciate and recognize [all contributors](https://github.com/avelino/awesome-go/graphs/contributors). We appreciate and recognize [all contributors](https://github.com/avelino/awesome-go/graphs/contributors).
# Contribution Guidelines # Contribution Guidelines
> Please be aware that we want to accept your contribution, but we have **some rules to keep the minimum quality** > Please be aware that we want to accept your contribution, but we have **some rules to keep the minimum quality** of the packages listed here. All reviews are **not personal feedback**, even if you are a _developer reviewing your contribution_. **Sorry, if we can't meet your expectations; we do our best**.
of the packages listed here. All reviews are **not personal feedback**,
even if you are a _developer reviewing your contribution_. **Sorry if we can't meet your expectations; we do our best**.
- **To add, remove, or change things on the list:** Submit a pull request - **To add, remove, or change things on the list:** Submit a pull request
To set this list apart from and complement the excellent [Go wiki Projects page](https://golang.org/wiki/Projects), To set this list apart from and complement the excellent [Go wiki Projects page](https://golang.org/wiki/Projects),
and other lists, awesome-go is a specially curated list for high-quality, actively maintained Go packages and resources. and other lists, awesome-go is a specially curated list of high-quality, actively maintained Go packages and resources.
Please contribute links to packages/projects you have used or are familiar with. This will help ensure high-quality entries. Please contribute links to packages/projects you have used or are familiar with. This will help ensure high-quality entries.
> the maintainers do not work full-time on the project, meaning that we do not have a set periodicity for reviewing contributions - rest assured that we will do our best to review and eventually accept contributions
## Quality standards ## Quality standards
To be on the list, project repositories should adhere to the following quality standards. To be on the list, project repositories should adhere to the following quality standards.
@ -32,6 +34,7 @@ To be on the list, project repositories should adhere to the following quality s
Categories must have at least 3 items. Categories must have at least 3 items.
## Preparing for review ## Preparing for review
Projects listed must have the following in their documentation. When submitting, you will be asked Projects listed must have the following in their documentation. When submitting, you will be asked
@ -43,9 +46,10 @@ to provide them.
One way to accomplish the above is to add badges to your project's README file. One way to accomplish the above is to add badges to your project's README file.
- Use https://pkg.go.dev/badge/ to create the pkg.go.dev link. - Use https://pkg.go.dev/badge/ to create the pkg.go.dev link.
- Go to https://goreportcard.com/ to generate a Go Report Card report, then click on the report badge in the upper right corner to see details on how to add the badge to your README. - Go to https://goreportcard.com/ to generate a Go Report Card report, then click on the report badge in the upper-right corner to see details on how to add the badge to your README.
- Codecov, coveralls, and gocover all offer ways to create badges for code coverage reports. Another option is to generate a badge as part of a continuous integration process. See [Code Coverage](COVERAGE.md) for an example. - Codecov, coveralls, and gocover all offer ways to create badges for code coverage reports. Another option is to generate a badge as part of a continuous integration process. See [Code Coverage](COVERAGE.md) for an example.
## How to add an item to the list ## How to add an item to the list
Open a pull request against the README.md document that adds the repository to the list. Open a pull request against the README.md document that adds the repository to the list.
@ -58,13 +62,13 @@ Open a pull request against the README.md document that adds the repository to t
- Remember to put a period `.` at the end of the project description. - Remember to put a period `.` at the end of the project description.
If you are creating a new category, move the projects that apply to the new category, ensuring If you are creating a new category, move the projects that apply to the new category, ensuring
that the resulting list has at least 3 projects in every category and that the categories are alphabetized. that the resulting list has at least 3 projects in every category, and that the categories are alphabetized.
Fill out the template in your PR with the links asked for. If you accidentally remove the PR template from the submission, you can find it [here](https://github.com/avelino/awesome-go/blob/main/.github/PULL_REQUEST_TEMPLATE.md). Fill out the template in your PR with the links asked for. If you accidentally remove the PR template from the submission, you can find it [here](https://github.com/avelino/awesome-go/blob/main/.github/PULL_REQUEST_TEMPLATE.md).
## Congrats, your project got accepted - what now? ## Congrats, your project got accepted - what now?
You are an awesome project now! Feel encouraged to tell others about it by adding one of these badges: You are an outstanding project now! Feel encouraged to tell others about it by adding one of these badges:
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go)
@ -72,6 +76,8 @@ You are an awesome project now! Feel encouraged to tell others about it by addin
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go)
``` ```
## Maintenance expectations for projects listed here ## Maintenance expectations for projects listed here
To prevent removal from awesome-go, your project must maintain the following quality standards. To prevent removal from awesome-go, your project must maintain the following quality standards.
@ -80,9 +86,10 @@ To prevent removal from awesome-go, your project must maintain the following qua
- All links to quality reports should be to the most recent official release or current ongoing development. - All links to quality reports should be to the most recent official release or current ongoing development.
Highly recommended but not required: Highly recommended but not required:
- A continuous integration process be part of the ongoing development process - A continuous integration process to be part of the ongoing development process
- That the project uses a pull-request process, and the owners do not commit directly to the repository - That the project uses a pull-request process, and the owners do not commit directly to the repository
- That the pull-request process requires the continuous-integration tests pass before a pull request can be merged - That the pull-request process requires the continuous-integration tests to pass before a pull request can be merged
## How to remove an item from the list ## How to remove an item from the list
@ -109,6 +116,7 @@ This project is scheduled to be removed within 2 weeks of this posting. To conti
Then, comment on your PR at awesome-go with a link to the removal issue at the project. Then, comment on your PR at awesome-go with a link to the removal issue at the project.
## Maintainers ## Maintainers
To make sure every PR is checked, we have [team maintainers](MAINTAINERS). Every PR MUST be reviewed by at least one maintainer before it can get merged. To make sure every PR is checked, we have [team maintainers](MAINTAINERS). Every PR MUST be reviewed by at least one maintainer before it can get merged.
@ -120,12 +128,41 @@ that the PR will be closed.
## Reporting issues ## Reporting issues
Please open an issue if you would like to discuss anything that could be improved or have suggestions for making the list a more valuable resource. We realize sometimes packages fall into abandonment or have breaking builds for extended periods of time, so if you see that, feel free to change its listing or let us know. We also realize that sometimes projects are just going through transitions or are more experimental in nature. These can still be cool, but we can indicate them as transitory or experimental. Please open an issue if you would like to discuss anything that could be improved or have suggestions for making the list a more valuable resource. We realize sometimes packages fall into abandonment or have breaking builds for extended periods of time, so if you see that, feel free to change its listing, or please let us know. We also realize that sometimes projects are just going through transitions or are more experimental in nature. These can still be cool, but we can indicate them as transitory or experimental.
Removal changes will not be applied until they have been pending for a minimum of 1 week (7 days). This grace window benefits projects that may be going through a temporary transition but are otherwise worthy of being on the list. Removal changes will not be applied until they have been pending for a minimum of 1 week (7 days). This grace window benefits projects that may be going through a temporary transition, but are otherwise worthy of being on the list.
Thanks, everyone! Thanks, everyone!
## How decision are made
## How decisions are made
The official group of maintainers has the final decision on what PRs are accepted. Discussions are made openly in issues. Decisions are made by consensus. The official group of maintainers has the final decision on what PRs are accepted. Discussions are made openly in issues. Decisions are made by consensus.
## How to become a contributor?
awesome-go is an open source project (created and maintained by the community), we are always open to new people to help us review the contributions (pull requests), **you don't need permission** or _name on the maintainers list_ to review a contribution and mark it as **LGTM**.
> Before you do anything, please read [this topic](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards) very carefully.
Now that you've read it, let's go!
Go into the pull requests (PR) and look at the following aspects:
* **shared links in the body of the PR:** they need to be valid and follow the quality specified above
* **check that the link added to `README.md`** is the same as the link to the repository mentioned in the body of the PR.
* **is it in the correct category?**
If everything is OK, mark the PR as approved, [read this documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/reviewing-proposed-changes-in-a-pull-request#starting-a-review) on how to do it.
**Welcome to awesome-go!**
## How to become an ~~"official maintainer"~~?
We don't give this name to people who are allowed to accept the PR.
If you are a person who is constantly active in reviewing PR and contributing to the project, you will be invited by a maintainer.
> **remember:** if you stop contributing with awesome-go for a long time, you will automatically be removed from the list of maintainers.

View File

@ -1,15 +1,10 @@
# Code Coverage # Code Coverage
While we recommend using one of the free websites available for monitoring code coverage While we recommend using one of the free websites available for monitoring code coverage during your continuous integration process, below is an example of how you can incorporate code coverage during the continuous integration process provided by GitHub actions and generate a code coverage report without one of those services.
during your continuous integration process, below is an example of how you can incorporate
code coverage during the continuous integration process provided by github actions and
generate a code coverage report without one of those services.
This yaml file will run tests on multiple system configurations, but will produce This `yaml` file will run tests on multiple system configurations, but will produce a code coverage report on only one of those. It will then create a code coverage badge and add it to the README file.
a code coverage report on only one of those. It will then create a code coverage badge
and add it to the README file.
This file should be put in the `.github/workflows` directory of your repo. This file should be put in the `.github/workflows` directory of your repo:
```yaml ```yaml
name: Go # The name of the workflow that will appear on Github name: Go # The name of the workflow that will appear on Github

666
README.md

File diff suppressed because it is too large Load Diff

21
go.mod
View File

@ -1,19 +1,22 @@
module github.com/avelino/awesome-go module github.com/avelino/awesome-go
go 1.17 go 1.21
require ( require (
github.com/PuerkitoBio/goquery v1.8.0 github.com/PuerkitoBio/goquery v1.8.1
github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774
github.com/yuin/goldmark v1.4.13 github.com/otiai10/copy v1.14.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 github.com/yuin/goldmark v1.6.0
golang.org/x/oauth2 v0.15.0
) )
require ( require (
github.com/andybalholm/cascadia v1.3.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/golang/protobuf v1.4.2 // indirect github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect golang.org/x/net v0.23.0 // indirect
golang.org/x/text v0.3.6 // indirect golang.org/x/sync v0.3.0 // indirect
google.golang.org/appengine v1.6.6 // indirect golang.org/x/sys v0.18.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect golang.org/x/text v0.14.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.33.0 // indirect
) )

398
go.sum
View File

@ -1,376 +1,68 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 h1:HrMVYtly2IVqg9EBooHsakQ256ueojP7QuG32K71X/U= github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 h1:HrMVYtly2IVqg9EBooHsakQ256ueojP7QuG32K71X/U=
github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774/go.mod h1:5wi5YYOpfuAKwL5XLFYopbgIl/v7NZxaJpa/4X6yFKE= github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774/go.mod h1:5wi5YYOpfuAKwL5XLFYopbgIl/v7NZxaJpa/4X6yFKE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

365
main.go Normal file
View File

@ -0,0 +1,365 @@
// Package main contains code for generate static site.
package main
import (
"bytes"
"embed"
"errors"
"fmt"
template2 "html/template"
"net/url"
"os"
"path/filepath"
"text/template"
"github.com/avelino/awesome-go/pkg/markdown"
cp "github.com/otiai10/copy"
"github.com/PuerkitoBio/goquery"
"github.com/avelino/awesome-go/pkg/slug"
)
// Link contains info about awesome url
type Link struct {
Title string
URL string
Description string
}
// Category describe link category
type Category struct {
Title string
Slug string
Description string
Links []Link
}
// Source files
const readmePath = "README.md"
// This files should be copied 'as is' to outDir directory
var staticFiles = []string{
"tmpl/assets",
"tmpl/robots.txt",
}
// Templates
//go:embed tmpl/*.tmpl.html tmpl/*.tmpl.xml
var tplFs embed.FS
var tpl = template.Must(template.ParseFS(tplFs, "tmpl/*.tmpl.html", "tmpl/*.tmpl.xml"))
// Output files
const outDir = "out/" // NOTE: trailing slash is required
var outIndexFile = filepath.Join(outDir, "index.html")
var outSitemapFile = filepath.Join(outDir, "sitemap.xml")
func main() {
if err := buildStaticSite(); err != nil {
panic(err)
}
}
func buildStaticSite() error {
if err := dropCreateDir(outDir); err != nil {
return fmt.Errorf("drop-create out dir: %w", err)
}
if err := renderIndex(readmePath, outIndexFile); err != nil {
return fmt.Errorf("convert markdown to html: %w", err)
}
input, err := os.ReadFile(outIndexFile)
if err != nil {
return fmt.Errorf("read converted html: %w", err)
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(input))
if err != nil {
return fmt.Errorf("create goquery instance: %w", err)
}
categories, err := extractCategories(doc)
if err != nil {
return fmt.Errorf("extract categories: %w", err)
}
if err := renderCategories(categories); err != nil {
return fmt.Errorf("render categories: %w", err)
}
if err := rewriteLinksInIndex(doc, categories); err != nil {
return fmt.Errorf("rewrite links in index: %w", err)
}
if err := renderSitemap(categories); err != nil {
return fmt.Errorf("render sitemap: %w", err)
}
for _, srcFilename := range staticFiles {
dstFilename := filepath.Join(outDir, filepath.Base(srcFilename))
fmt.Printf("Copy static file: %s -> %s\n", srcFilename, dstFilename)
if err := cp.Copy(srcFilename, dstFilename); err != nil {
return fmt.Errorf("copy static file `%s` to `%s`: %w", srcFilename, dstFilename, err)
}
}
return nil
}
// dropCreateDir drop and create output directory
func dropCreateDir(dir string) error {
if err := os.RemoveAll(dir); err != nil {
return fmt.Errorf("remove dir: %w", err)
}
if err := mkdirAll(dir); err != nil {
return fmt.Errorf("create dir: %w", err)
}
return nil
}
func mkdirAll(path string) error {
_, err := os.Stat(path)
// directory is exists
if err == nil {
return nil
}
// unexpected error
if !os.IsNotExist(err) {
return fmt.Errorf("unexpected result of dir stat: %w", err)
}
// directory is not exists
if err := os.MkdirAll(path, 0755); err != nil {
return fmt.Errorf("midirAll: %w", err)
}
return nil
}
func renderCategories(categories map[string]Category) error {
for _, category := range categories {
categoryDir := filepath.Join(outDir, category.Slug)
if err := mkdirAll(categoryDir); err != nil {
return fmt.Errorf("create category dir `%s`: %w", categoryDir, err)
}
// FIXME: embed templates
categoryIndexFilename := filepath.Join(categoryDir, "index.html")
fmt.Printf("Write category Index file: %s\n", categoryIndexFilename)
buf := bytes.NewBuffer(nil)
if err := tpl.Lookup("category-index.tmpl.html").Execute(buf, category); err != nil {
return fmt.Errorf("render category `%s`: %w", categoryDir, err)
}
// Sanitize HTML. This is not necessary, but allows to have content
// of all html files in same style.
{
doc, err := goquery.NewDocumentFromReader(buf)
if err != nil {
return fmt.Errorf("create goquery instance for `%s`: %w", categoryDir, err)
}
html, err := doc.Html()
if err != nil {
return fmt.Errorf("render goquery html for `%s`: %w", categoryDir, err)
}
if err := os.WriteFile(categoryIndexFilename, []byte(html), 0644); err != nil {
return fmt.Errorf("write category file `%s`: %w", categoryDir, err)
}
}
}
return nil
}
func renderSitemap(categories map[string]Category) error {
f, err := os.Create(outSitemapFile)
if err != nil {
return fmt.Errorf("create sitemap file `%s`: %w", outSitemapFile, err)
}
fmt.Printf("Render Sitemap to: %s\n", outSitemapFile)
if err := tpl.Lookup("sitemap.tmpl.xml").Execute(f, categories); err != nil {
return fmt.Errorf("render sitemap: %w", err)
}
return nil
}
func extractCategories(doc *goquery.Document) (map[string]Category, error) {
categories := make(map[string]Category)
var rootErr error
doc.
Find("body #contents").
NextFiltered("ul").
Find("ul").
EachWithBreak(func(_ int, selUl *goquery.Selection) bool {
if rootErr != nil {
return false
}
selUl.
Find("li a").
EachWithBreak(func(_ int, s *goquery.Selection) bool {
selector, exists := s.Attr("href")
if !exists {
return true
}
category, err := extractCategory(doc, selector)
if err != nil {
rootErr = fmt.Errorf("extract category: %w", err)
return false
}
categories[selector] = *category
return true
})
return true
})
if rootErr != nil {
return nil, fmt.Errorf("extract categories: %w", rootErr)
}
return categories, nil
}
func extractCategory(doc *goquery.Document, selector string) (*Category, error) {
var category Category
var err error
doc.Find(selector).EachWithBreak(func(_ int, selCatHeader *goquery.Selection) bool {
selDescr := selCatHeader.NextFiltered("p")
// FIXME: bug. this would select links from all neighboring
// sub-categories until the next category. To prevent this we should
// find only first ul
ul := selCatHeader.NextFilteredUntil("ul", "h2")
var links []Link
ul.Find("li").Each(func(_ int, selLi *goquery.Selection) {
selLink := selLi.Find("a")
url, _ := selLink.Attr("href")
link := Link{
Title: selLink.Text(),
// FIXME(kazhuravlev): Title contains only title but
// description contains Title + description
Description: selLi.Text(),
URL: url,
}
links = append(links, link)
})
// FIXME: In this case we would have an empty category in main index.html with link to 404 page.
if len(links) == 0 {
err = errors.New("category does not contain links")
return false
}
category = Category{
Slug: slug.Generate(selCatHeader.Text()),
Title: selCatHeader.Text(),
Description: selDescr.Text(),
Links: links,
}
return true
})
if err != nil {
return nil, fmt.Errorf("build a category: %w", err)
}
return &category, nil
}
func rewriteLinksInIndex(doc *goquery.Document, categories map[string]Category) error {
var iterErr error
doc.
Find("body #content ul li ul li a").
EachWithBreak(func(_ int, s *goquery.Selection) bool {
href, hrefExists := s.Attr("href")
if !hrefExists {
// FIXME: looks like is an error. Tag `a` in our case always
// should have `href` attr.
return true
}
// do not replace links if no page has been created for it
_, catExists := categories[href]
if !catExists {
return true
}
linkURL, err := url.Parse(href)
if err != nil {
iterErr = err
return false
}
if linkURL.Fragment != "" && linkURL.Fragment != "contents" {
s.SetAttr("href", linkURL.Fragment)
}
return true
})
if iterErr != nil {
return iterErr
}
fmt.Printf("Rewrite links in Index file: %s\n", outIndexFile)
resultHTML, err := doc.Html()
if err != nil {
return fmt.Errorf("render html: %w", err)
}
if err := os.WriteFile(outIndexFile, []byte(resultHTML), 0644); err != nil {
return fmt.Errorf("rewrite index file: %w", err)
}
return nil
}
// renderIndex generate site html (index.html) from markdown file
func renderIndex(srcFilename, outFilename string) error {
input, err := os.ReadFile(srcFilename)
if err != nil {
return err
}
body, err := markdown.ToHTML(input)
if err != nil {
return err
}
f, err := os.Create(outFilename)
if err != nil {
return err
}
fmt.Printf("Write Index file: %s\n", outIndexFile)
data := map[string]interface{}{
"Body": template2.HTML(body),
}
if err := tpl.Lookup("index.tmpl.html").Execute(f, data); err != nil {
return err
}
if err := f.Close(); err != nil {
return fmt.Errorf("close index file: %w", err)
}
return nil
}

View File

@ -1,7 +1,9 @@
package main package main
import ( import (
"io/ioutil" "bytes"
"github.com/avelino/awesome-go/pkg/markdown"
"os"
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
@ -16,9 +18,38 @@ var (
reLinkWithDescription = regexp.MustCompile(`\* \[.*\]\(.*\) - \S.*[\.\!]`) reLinkWithDescription = regexp.MustCompile(`\* \[.*\]\(.*\) - \S.*[\.\!]`)
) )
func requireNoErr(t *testing.T, err error, msg string) {
// FIXME: replace to github.com/stretchr/testify
t.Helper()
if msg == "" {
msg = "unknown error"
}
if err != nil {
t.Fatalf("Received unexpected error [%s]: %+v", msg, err)
}
}
func goqueryFromReadme(t *testing.T) *goquery.Document {
t.Helper()
input, err := os.ReadFile(readmePath)
requireNoErr(t, err, "readme file should be exists")
html, err := markdown.ToHTML(input)
requireNoErr(t, err, "markdown should be rendered to html")
buf := bytes.NewBuffer(html)
doc, err := goquery.NewDocumentFromReader(buf)
requireNoErr(t, err, "html must be valid for goquery")
return doc
}
func TestAlpha(t *testing.T) { func TestAlpha(t *testing.T) {
query := startQuery() doc := goqueryFromReadme(t)
query.Find("body > ul").Each(func(i int, s *goquery.Selection) { doc.Find("body > ul").Each(func(i int, s *goquery.Selection) {
if i != 0 { if i != 0 {
// skip content menu // skip content menu
// TODO: the sub items (with 3 hash marks `###`) are staying in // TODO: the sub items (with 3 hash marks `###`) are staying in
@ -30,9 +61,9 @@ func TestAlpha(t *testing.T) {
} }
func TestDuplicatedLinks(t *testing.T) { func TestDuplicatedLinks(t *testing.T) {
query := startQuery() doc := goqueryFromReadme(t)
links := make(map[string]bool, 0) links := make(map[string]bool, 0)
query.Find("body li > a:first-child").Each(func(_ int, s *goquery.Selection) { doc.Find("body li > a:first-child").Each(func(_ int, s *goquery.Selection) {
t.Run(s.Text(), func(t *testing.T) { t.Run(s.Text(), func(t *testing.T) {
href, ok := s.Attr("href") href, ok := s.Attr("href")
if !ok { if !ok {
@ -49,10 +80,9 @@ func TestDuplicatedLinks(t *testing.T) {
// Test if an entry has description, it must be separated from link with ` - ` // Test if an entry has description, it must be separated from link with ` - `
func TestSeparator(t *testing.T) { func TestSeparator(t *testing.T) {
var matched, containsLink, noDescription bool var matched, containsLink, noDescription bool
input, err := ioutil.ReadFile("./README.md") input, err := os.ReadFile(readmePath)
if err != nil { requireNoErr(t, err, "readme should be exists")
panic(err)
}
lines := strings.Split(string(input), "\n") lines := strings.Split(string(input), "\n")
for _, line := range lines { for _, line := range lines {
line = strings.Trim(line, " ") line = strings.Trim(line, " ")
@ -69,11 +99,12 @@ func TestSeparator(t *testing.T) {
} }
} }
} }
func TestGenerateHTML(t *testing.T) {
err := GenerateHTML() func TestRenderIndex(t *testing.T) {
if err != nil { requireNoErr(t, mkdirAll(outDir), "output dir should exists")
t.Errorf("html generate error '%s'", err.Error())
} err := renderIndex(readmePath, outIndexFile)
requireNoErr(t, err, "html should be rendered")
} }
func testList(t *testing.T, list *goquery.Selection) { func testList(t *testing.T, list *goquery.Selection) {

View File

@ -1,133 +0,0 @@
package main
import (
"bytes"
"fmt"
"log"
"os"
"strings"
"text/template"
"github.com/PuerkitoBio/goquery"
"github.com/avelino/awesome-go/pkg/slug"
)
type Link struct {
Title string
Url string
Description string
}
type Object struct {
Title string
Slug string
Description string
Items []Link
}
func main() {
err := GenerateHTML()
if err != nil {
panic(err)
}
input, err := os.ReadFile("./tmpl/index.html")
if err != nil {
panic(err)
}
buf := bytes.NewBuffer(input)
query, err := goquery.NewDocumentFromReader(buf)
if err != nil {
panic(err)
}
objs := make(map[string]*Object)
query.Find("body #contents").NextFiltered("ul").Find("ul").Each(func(_ int, s *goquery.Selection) {
s.Find("li a").Each(func(_ int, s *goquery.Selection) {
selector, exists := s.Attr("href")
if !exists {
return
}
obj := makeObjByID(selector, query.Find("body"))
if obj == nil {
return
}
objs[selector] = obj
})
})
makeSiteStruct(objs)
changeLinksInIndex(string(input), query, objs)
makeSitemap(objs)
}
func makeSiteStruct(objs map[string]*Object) {
for _, obj := range objs {
folder := fmt.Sprintf("tmpl/%s", obj.Slug)
err := os.Mkdir(folder, 0755)
if err != nil {
log.Println(err)
}
t := template.Must(template.ParseFiles("tmpl/cat-tmpl.html"))
f, _ := os.Create(fmt.Sprintf("%s/index.html", folder))
t.Execute(f, obj)
}
}
func makeSitemap(objs map[string]*Object) {
t := template.Must(template.ParseFiles("tmpl/sitemap-tmpl.xml"))
f, _ := os.Create("tmpl/sitemap.xml")
t.Execute(f, objs)
}
func makeObjByID(selector string, s *goquery.Selection) (obj *Object) {
s.Find(selector).Each(func(_ int, s *goquery.Selection) {
desc := s.NextFiltered("p")
ul := s.NextFilteredUntil("ul", "h2")
links := []Link{}
ul.Find("li").Each(func(_ int, s *goquery.Selection) {
url, _ := s.Find("a").Attr("href")
link := Link{
Title: s.Find("a").Text(),
Description: s.Text(),
Url: url,
}
links = append(links, link)
})
if len(links) == 0 {
return
}
obj = &Object{
Slug: slug.Generate(s.Text()),
Title: s.Text(),
Description: desc.Text(),
Items: links,
}
})
return
}
func changeLinksInIndex(html string, query *goquery.Document, objs map[string]*Object) {
query.Find("body #content ul li ul li a").Each(func(_ int, s *goquery.Selection) {
href, hrefExists := s.Attr("href")
if !hrefExists {
return
}
// do not replace links if no page has been created for it
_, objExists := objs[href]
if !objExists {
return
}
uri := strings.SplitAfter(href, "#")
if len(uri) >= 2 && uri[1] != "contents" {
html = strings.ReplaceAll(
html, fmt.Sprintf(`href="%s"`, href), fmt.Sprintf(`href="%s"`, uri[1]))
}
})
os.WriteFile("./tmpl/index.html", []byte(html), 0644)
}

47
netlify.toml Normal file
View File

@ -0,0 +1,47 @@
# Settings in the [build] context are global and are applied to
# all contexts unless otherwise overridden by more specific contexts.
[build]
# Directory to change to before starting a build.
# This is where we will look for package.json/.nvmrc/etc.
# If not set, defaults to the root directory.
base = "./"
# Directory that contains the deploy-ready HTML files and
# assets generated by the build. This is relative to the base
# directory if one has been set, or the root directory if
# a base has not been set. This sample publishes the directory
# located at the absolute path "root/project/build-output"
publish = "out/"
# Default build command.
command = "echo 'default context'"
[[plugins]]
# Installs the Lighthouse Build Plugin for all deploy contexts
package = "@netlify/plugin-lighthouse"
# Production context: all deploys from the Production branch
# set in your sites Branches settings in the UI will inherit
# these settings. You can define environment variables
# here but we recommend using the Netlify UI for sensitive
# values to keep them out of your source repository.
[context.production]
publish = "out/"
# Redirects and headers are GLOBAL for all builds they do not
# get scoped to contexts no matter where you define them in the file.
# For context-specific rules, use _headers or _redirects files,
# which are PER-DEPLOY.
[[redirects]]
from = "/awesome-cloud-native"
to = "/"
status = 302
force = true
[[redirects]]
from = "/awesome-go-fork"
to = "/"
status = 302
force = true

View File

@ -12,8 +12,8 @@ import (
"github.com/yuin/goldmark/util" "github.com/yuin/goldmark/util"
) )
// ConvertMarkdownToHTML converts markdown byte slice to a HTML byte slice // ToHTML converts markdown byte slice to a HTML byte slice
func ConvertMarkdownToHTML(markdown []byte) ([]byte, error) { func ToHTML(markdown []byte) ([]byte, error) {
md := goldmark.New( md := goldmark.New(
goldmark.WithExtensions(extension.GFM), goldmark.WithExtensions(extension.GFM),
goldmark.WithParserOptions( goldmark.WithParserOptions(

View File

@ -5,7 +5,7 @@ import (
"testing" "testing"
) )
func TestConvertMarkdownToHTML(t *testing.T) { func TestToHTML(t *testing.T) {
input := []byte( input := []byte(
`## some headline `## some headline
followed by some paragraph with [a link](https://example.local) followed by some paragraph with [a link](https://example.local)
@ -46,12 +46,12 @@ and some list:</p>
<p><a href="https://example.local">embedded HTML is allowed</a></p>`, <p><a href="https://example.local">embedded HTML is allowed</a></p>`,
) )
got, err := ConvertMarkdownToHTML(input) got, err := ToHTML(input)
if err != nil { if err != nil {
t.Errorf("ConvertMarkdownToHTML() error = %v", err) t.Errorf("ToHTML() error = %v", err)
return return
} }
if strings.TrimSpace(string(got)) != strings.TrimSpace(string(expected)) { if strings.TrimSpace(string(got)) != strings.TrimSpace(string(expected)) {
t.Errorf("ConvertMarkdownToHTML() got = %v, want %v", string(got), string(expected)) t.Errorf("ToHTML() got = %v, want %v", string(got), string(expected))
} }
} }

View File

@ -8,6 +8,7 @@ import (
// Generate slugs similar to GitHub's slugs on markdown parsing // Generate slugs similar to GitHub's slugs on markdown parsing
func Generate(text string) string { func Generate(text string) string {
// FIXME: this is should be like regexp.Replace(`[^-a-zA-Z\d]+`, ``)
s := strings.ReplaceAll(text, "/", "") s := strings.ReplaceAll(text, "/", "")
return slugify.Slugify(strings.TrimSpace(s)) return slugify.Slugify(strings.TrimSpace(s))
} }

View File

@ -1,51 +0,0 @@
package main
import (
"bytes"
"html/template"
"io/ioutil"
"os"
"github.com/PuerkitoBio/goquery"
"github.com/avelino/awesome-go/pkg/markdown"
)
func readme() []byte {
input, err := os.ReadFile("./README.md")
if err != nil {
panic(err)
}
html, err := markdown.ConvertMarkdownToHTML(input)
if err != nil {
panic(err)
}
return html
}
func startQuery() *goquery.Document {
buf := bytes.NewBuffer(readme())
query, err := goquery.NewDocumentFromReader(buf)
if err != nil {
panic(err)
}
return query
}
type content struct {
Body template.HTML
}
// GenerateHTML generate site html (index.html) from markdown file
func GenerateHTML() (err error) {
// options
readmePath := "./README.md"
tplPath := "tmpl/tmpl.html"
idxPath := "tmpl/index.html"
input, _ := ioutil.ReadFile(readmePath)
body, _ := markdown.ConvertMarkdownToHTML(input)
c := &content{Body: template.HTML(body)}
t := template.Must(template.ParseFiles(tplPath))
f, err := os.Create(idxPath)
t.Execute(f, c)
return
}

View File

@ -18,211 +18,250 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
const issueTemplate = ` const issueTemplateContent = `
{{range .}} {{range .}}
- [ ] {{.}} - [ ] {{.}}
{{end}} {{end}}
` `
var issueTemplate = template.Must(template.New("issue").Parse(issueTemplateContent))
// FIXME: use official github client
var reGithubRepo = regexp.MustCompile("https://github.com/[a-zA-Z0-9-._]+/[a-zA-Z0-9-._]+$") var reGithubRepo = regexp.MustCompile("https://github.com/[a-zA-Z0-9-._]+/[a-zA-Z0-9-._]+$")
var githubGETREPO = "https://api.github.com/repos%s" var githubGETREPO = "https://api.github.com/repos%s"
var githubGETCOMMITS = "https://api.github.com/repos%s/commits" var githubGETCOMMITS = "https://api.github.com/repos%s/commits"
var githubPOSTISSUES = "https://api.github.com/repos/avelino/awesome-go/issues" var githubPOSTISSUES = "https://api.github.com/repos/avelino/awesome-go/issues"
// FIXME: use https
var awesomeGoGETISSUES = "http://api.github.com/repos/avelino/awesome-go/issues" //only returns open issues var awesomeGoGETISSUES = "http://api.github.com/repos/avelino/awesome-go/issues" //only returns open issues
// FIXME: variable has type Duration, but contains a number. we should use
//
// time.Hour * ... or change type of variable
var numberOfYears time.Duration = 1 var numberOfYears time.Duration = 1
var timeNow = time.Now() var timeNow = time.Now()
var issueTitle = fmt.Sprintf("Investigate repositories with more than 1 year without update - %s", timeNow.Format("2006-01-02")) var issueTitle = fmt.Sprintf("Investigate repositories with more than 1 year without update - %s", timeNow.Format(time.DateOnly))
const deadLinkMessage = " this repository might no longer exist! (status code >= 400 returned)" const deadLinkMessage = " this repository might no longer exist! (status code >= 400 returned)"
const movedPermanently = " status code 301 received" const movedPermanently = " status code 301 received"
const status302 = " status code 302 received" const status302 = " status code 302 received"
const archived = " repository has been archived" const archived = " repository has been archived"
//LIMIT specifies the max number of repositories that are added in a single run of the script // LIMIT specifies the max number of repositories that are added in a single run of the script
var LIMIT = 10 var LIMIT = 10
var ctr = 0 var ctr = 0
type tokenSource struct { type tokenSource struct {
AccessToken string AccessToken string
} }
type issue struct { type issue struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
} }
type repo struct {
Archived bool `json:"archived"`
}
func (t *tokenSource) Token() (*oauth2.Token, error) { func (t *tokenSource) Token() (*oauth2.Token, error) {
token := &oauth2.Token{ return &oauth2.Token{
AccessToken: t.AccessToken, AccessToken: t.AccessToken,
} }, nil
return token, nil
} }
func getRepositoriesFromBody(body string) []string { func getRepositoriesFromBody(body string) []string {
links := strings.Split(body, "- ") links := strings.Split(body, "- ")
for idx, link := range links { for i, link := range links {
str := strings.ReplaceAll(link, "\r", "") link = strings.ReplaceAll(link, "\r", "")
str = strings.ReplaceAll(str, "[ ]", "") link = strings.ReplaceAll(link, "[ ]", "")
str = strings.ReplaceAll(str, "[x]", "") link = strings.ReplaceAll(link, "[x]", "")
str = strings.ReplaceAll(str, " ", "") link = strings.ReplaceAll(link, " ", "")
str = strings.ReplaceAll(str, "\n", "") link = strings.ReplaceAll(link, "\n", "")
str = strings.ReplaceAll(str, deadLinkMessage, "") link = strings.ReplaceAll(link, deadLinkMessage, "")
str = strings.ReplaceAll(str, movedPermanently, "") link = strings.ReplaceAll(link, movedPermanently, "")
str = strings.ReplaceAll(str, status302, "") link = strings.ReplaceAll(link, status302, "")
str = strings.ReplaceAll(str, archived, "") link = strings.ReplaceAll(link, archived, "")
links[idx] = str links[i] = link
} }
return links return links
} }
func generateIssueBody(repositories []string) (string, error) {
var writer bytes.Buffer func generateIssueBody(t *testing.T, repositories []string) (string, error) {
t := template.New("issue") t.Helper()
temp, err := t.Parse(issueTemplate)
if err != nil { buf := bytes.NewBuffer(nil)
log.Print("Failed to generate template") err := issueTemplate.Execute(buf, repositories)
return "", err requireNoErr(t, err, "Failed to generate template")
}
err = temp.Execute(&writer, repositories) return buf.String(), nil
if err != nil {
log.Print("Failed to generate template")
return "", err
}
issueBody := writer.String()
return issueBody, nil
} }
func createIssue(staleRepos []string, client *http.Client) {
func createIssue(t *testing.T, staleRepos []string, client *http.Client) {
t.Helper()
if len(staleRepos) == 0 { if len(staleRepos) == 0 {
log.Print("NO STALE REPOSITORIES") log.Print("NO STALE REPOSITORIES")
return return
} }
body, err := generateIssueBody(staleRepos)
if err != nil { body, err := generateIssueBody(t, staleRepos)
log.Print("Failed at CreateIssue") requireNoErr(t, err, "failed to generate issue body")
return
}
newIssue := &issue{ newIssue := &issue{
Title: issueTitle, Title: issueTitle,
Body: body, Body: body,
} }
buf := new(bytes.Buffer) buf := bytes.NewBuffer(nil)
json.NewEncoder(buf).Encode(newIssue) requireNoErr(t, json.NewEncoder(buf).Encode(newIssue), "failed to encode json req")
req, err := http.NewRequest("POST", githubPOSTISSUES, buf)
if err != nil { req, err := http.NewRequest(http.MethodPost, githubPOSTISSUES, buf)
log.Print("Failed at CreateIssue") requireNoErr(t, err, "failed to create request")
return
} _, roundTripErr := client.Do(req)
client.Do(req) requireNoErr(t, roundTripErr, "failed to send request")
} }
func getAllFlaggedRepositories(client *http.Client, flaggedRepositories *map[string]bool) error {
req, err := http.NewRequest("GET", awesomeGoGETISSUES, nil) func getAllFlaggedRepositories(t *testing.T, client *http.Client) map[string]bool {
if err != nil { t.Helper()
log.Print("Failed to get all issues")
return err req, err := http.NewRequest(http.MethodGet, awesomeGoGETISSUES, nil)
} requireNoErr(t, err, "failed to create request")
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { requireNoErr(t, err, "failed to send request")
log.Print("Failed to get all issues")
return err
}
target := []issue{}
defer res.Body.Close() defer res.Body.Close()
json.NewDecoder(res.Body).Decode(&target)
for _, i := range target { var issues []issue
if i.Title == issueTitle { requireNoErr(t, json.NewDecoder(res.Body).Decode(&issues), "failed to unmarshal response")
repos := getRepositoriesFromBody(i.Body)
for _, repo := range repos {
(*flaggedRepositories)[repo] = true
}
}
}
return nil
}
func containsOpenIssue(link string, openIssues map[string]bool) bool {
_, ok := openIssues[link]
return ok
}
func testRepoState(toRun bool, href string, client *http.Client, staleRepos *[]string) bool {
if toRun {
ownerRepo := strings.ReplaceAll(href, "https://github.com", "")
apiCall := fmt.Sprintf(githubGETREPO, ownerRepo)
req, err := http.NewRequest("GET", apiCall, nil)
var repoResp repo
isRepoAdded := false
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
}
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&repoResp)
if resp.StatusCode == http.StatusMovedPermanently {
*staleRepos = append(*staleRepos, href+movedPermanently)
log.Printf("%s returned %d", href, resp.StatusCode)
isRepoAdded = true
}
if resp.StatusCode == http.StatusFound && !isRepoAdded {
*staleRepos = append(*staleRepos, href+status302)
log.Printf("%s returned %d", href, resp.StatusCode)
isRepoAdded = true
}
if resp.StatusCode >= http.StatusBadRequest && !isRepoAdded {
*staleRepos = append(*staleRepos, href+deadLinkMessage)
log.Printf("%s might not exist!", href)
isRepoAdded = true
}
if repoResp.Archived && !isRepoAdded {
*staleRepos = append(*staleRepos, href+archived)
log.Printf("%s is archived!", href)
isRepoAdded = true
}
return isRepoAdded
}
return false
}
func testCommitAge(toRun bool, href string, client *http.Client, staleRepos *[]string) bool {
if toRun {
var respObj []map[string]interface{}
since := timeNow.Add(-1 * 365 * 24 * numberOfYears * time.Hour)
sinceQuery := since.Format(time.RFC3339)
ownerRepo := strings.ReplaceAll(href, "https://github.com", "")
apiCall := fmt.Sprintf(githubGETCOMMITS, ownerRepo)
req, err := http.NewRequest("GET", apiCall, nil)
isRepoAdded := false
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
}
q := req.URL.Query()
q.Add("since", sinceQuery)
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
}
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&respObj)
isAged := len(respObj) == 0
if isAged {
log.Printf("%s has not had a commit in a while", href)
*staleRepos = append(*staleRepos, href)
isRepoAdded = true
}
return isRepoAdded
}
return false
}
func testStaleRepository() {
query := startQuery()
var staleRepos []string
addressedRepositories := make(map[string]bool) addressedRepositories := make(map[string]bool)
for _, issue := range issues {
if issue.Title != issueTitle {
continue
}
repos := getRepositoriesFromBody(issue.Body)
for _, repo := range repos {
addressedRepositories[repo] = true
}
}
return addressedRepositories
}
func checkRepoAvailability(toRun bool, href string, client *http.Client) ([]string, bool) {
if !toRun {
return nil, false
}
ownerRepo := strings.ReplaceAll(href, "https://github.com", "")
apiCall := fmt.Sprintf(githubGETREPO, ownerRepo)
req, err := http.NewRequest(http.MethodGet, apiCall, nil)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return nil, false
}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return nil, false
}
defer resp.Body.Close()
var repoResp struct {
Archived bool `json:"archived"`
}
if err := json.NewDecoder(resp.Body).Decode(&repoResp); err != nil {
return nil, false
}
var isRepoAdded bool
var warnings []string
if resp.StatusCode == http.StatusMovedPermanently {
warnings = append(warnings, href+movedPermanently)
log.Printf("%s returned %d", href, resp.StatusCode)
isRepoAdded = true
}
if resp.StatusCode == http.StatusFound && !isRepoAdded {
warnings = append(warnings, href+status302)
log.Printf("%s returned %d", href, resp.StatusCode)
isRepoAdded = true
}
if resp.StatusCode >= http.StatusBadRequest && !isRepoAdded {
warnings = append(warnings, href+deadLinkMessage)
log.Printf("%s might not exist!", href)
isRepoAdded = true
}
if repoResp.Archived && !isRepoAdded {
warnings = append(warnings, href+archived)
log.Printf("%s is archived!", href)
isRepoAdded = true
}
// FIXME: expression `(len(warnings) > 0) == isRepoAdded` is always true.
return warnings, isRepoAdded
}
func checkRepoCommitActivity(toRun bool, href string, client *http.Client) ([]string, bool) {
if !toRun {
return nil, false
}
ownerRepo := strings.ReplaceAll(href, "https://github.com", "")
apiCall := fmt.Sprintf(githubGETCOMMITS, ownerRepo)
req, err := http.NewRequest(http.MethodGet, apiCall, nil)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return nil, false
}
since := timeNow.Add(-1 * 365 * 24 * numberOfYears * time.Hour)
sinceQuery := since.Format(time.RFC3339)
q := req.URL.Query()
q.Add("since", sinceQuery)
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return nil, false
}
defer resp.Body.Close()
var respObj []map[string]interface{}
// FIXME: handle error in all that cases
if err := json.NewDecoder(resp.Body).Decode(&respObj); err != nil {
return nil, false
}
var warnings []string
var isRepoAdded bool
isAged := len(respObj) == 0
if isAged {
log.Printf("%s has not had a commit in a while", href)
warnings = append(warnings, href)
isRepoAdded = true
}
// FIXME: expression `(len(warnings) > 0) == isRepoAdded` is always true.
return warnings, isRepoAdded
}
func TestStaleRepository(t *testing.T) {
doc := goqueryFromReadme(t)
oauth := os.Getenv("OAUTH_TOKEN") oauth := os.Getenv("OAUTH_TOKEN")
client := &http.Client{} client := &http.Client{
Transport: &http.Transport{},
}
if oauth == "" { if oauth == "" {
log.Print("No oauth token found. Using unauthenticated client ...") log.Print("No oauth token found. Using unauthenticated client ...")
} else { } else {
@ -231,42 +270,47 @@ func testStaleRepository() {
} }
client = oauth2.NewClient(context.Background(), tokenSource) client = oauth2.NewClient(context.Background(), tokenSource)
} }
err := getAllFlaggedRepositories(client, &addressedRepositories)
if err != nil { // FIXME: return addressedRepositories, no need to pass
log.Println("Failed to get existing issues. Exiting...") addressedRepositories := getAllFlaggedRepositories(t, client)
return
} var staleRepos []string
query.Find("body li > a:first-child").EachWithBreak(func(_ int, s *goquery.Selection) bool { doc.
href, ok := s.Attr("href") Find("body li > a:first-child").
if !ok { EachWithBreak(func(_ int, s *goquery.Selection) bool {
log.Println("expected to have href") href, ok := s.Attr("href")
return true if !ok {
} log.Println("expected to have href")
if ctr >= LIMIT && LIMIT != -1 { return true
log.Print("Max number of issues created") }
return false
} if ctr >= LIMIT && LIMIT != -1 {
issueExists := containsOpenIssue(href, addressedRepositories) log.Print("Max number of issues created")
if issueExists { return false
log.Printf("issue already exists for %s\n", href) }
} else {
isGithubRepo := reGithubRepo.MatchString(href) if _, issueExists := addressedRepositories[href]; issueExists {
if isGithubRepo { log.Printf("issue already exists for %s\n", href)
isRepoAdded := testRepoState(true, href, client, &staleRepos) return true
isRepoAdded = testCommitAge(!isRepoAdded, href, client, &staleRepos) }
if isRepoAdded {
ctr++ if !reGithubRepo.MatchString(href) {
}
} else {
log.Printf("%s non-github repo not currently handled", href) log.Printf("%s non-github repo not currently handled", href)
} }
}
return true
})
createIssue(staleRepos, client)
}
func TestStaleRepository(t *testing.T) { // FIXME: this is `or` expres24sion. Probably we need `and`?
testStaleRepository() warnings, isRepoAdded := checkRepoAvailability(true, href, client)
staleRepos = append(staleRepos, warnings...)
warnings, isRepoAdded = checkRepoCommitActivity(!isRepoAdded, href, client)
staleRepos = append(staleRepos, warnings...)
if isRepoAdded {
ctr++
}
return true
})
createIssue(t, staleRepos, client)
} }

View File

@ -1,2 +0,0 @@
/awesome-cloud-native/ /
/awesome-go-fork/ /

View File

@ -114,3 +114,7 @@ h1 > a img {
font-size: 9px; font-size: 9px;
line-height: 1; line-height: 1;
} }
td {
padding: 6px;
}

115
tmpl/cat-tmpl.html vendored
View File

@ -1,115 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Language" content="en">
<meta name="viewport" content="width=device-width">
<title>{{.Title}} - Awesome Go / Golang</title>
<meta name="description" content="{{.Description}} - Awesome Go / Golang">
<meta name="keywords" content="{{.Title}}, golang, go, awesome, awesome-go, go framework, golang framework">
<meta name="twitter:card" value="summary">
<meta property="og:title" content="{{.Description}} - Awesome Go" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://awesome-go.com/{{.Slug}}" />
<meta property="og:image" content="https://awesome-go.com/assets/logo.png" />
<meta property="og:description" content="{{.Description}} - Awesome Go" />
<link rel="canonical" href="https://awesome-go.com/{{.Slug}}">
<link rel="stylesheet" type="text/css" href="/assets/fonts/firasans.css">
<link rel="stylesheet" type="text/css" href="/assets/normalize.css">
<link rel="stylesheet" type="text/css" href="/assets/awesome-go.css">
<!--ICONS-->
<link rel="icon" href="./assets/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="./assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="./assets/favicon/manifest.json">
</head>
<body>
<div id="content">
<header>
<h1>
<a href="https://awesome-go.com/">
<img align="right" src="https://github.com/avelino/awesome-go/raw/main/tmpl/assets/logo.png" alt="awesome-go" title="awesome-go" />
</a>
{{.Title}} - <a href="https://awesome-go.com/">Awesome Go</a>
</h1>
<p><em>{{.Description}}</em></p>
<p>
<a href="https://github.com/avelino/awesome-go/actions/workflows/tests.yaml?query=branch%3Amain" rel="nofollow"><img src="https://github.com/avelino/awesome-go/actions/workflows/tests.yaml/badge.svg?branch=main" alt="Build Status"></a>
<a href="https://github.com/sindresorhus/awesome" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></a>
<a href="https://gophers.slack.com/messages/awesome" rel="nofollow"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&amp;logo=slack&amp;colorB=red" alt="Slack Widget"></a>
<a href="https://app.netlify.com/sites/awesome-go/deploys" rel="nofollow"><img src="https://api.netlify.com/api/v1/badges/83a6dcbe-0da6-433e-b586-f68109286bd5/deploy-status" alt="Netlify Status"></a>
<a href="https://www.trackawesomelist.com/avelino/awesome-go/" rel="nofollow"><img src="https://www.trackawesomelist.com/badge.svg" alt="Track Awesome List"></a>
</p>
<p>
<a href="https://www.producthunt.com/posts/awesome-go?utm_source=badge-featured&amp;utm_medium=badge&amp;utm_souce=badge-awesome-go" rel="nofollow"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291535&amp;theme=light" width="250" height="54" /></a>
</p>
</header>
<div>
<h3 id="sponsorships">Sponsorships</h3>
<p>
<div id="amzn-assoc-ad-ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></div>
<script async src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US&adInstanceId=ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></script>
</p>
<p>
<a href="https://www.doppler.com/?utm_campaign=github_repo&amp;utm_medium=referral&amp;utm_content=awesomego&amp;utm_source=github">
<img src="https://github.com/avelino/awesome-go/raw/main/tmpl/assets/sponsors/doppler.png" alt="Doppler">
<br />
<b>Quit struggling with scattered API keys and access controls.</b>
</a>
</p>
<p>
<a href="https://bit.ly/awesome-go-xteam">
<img src="https://avelino.run/sponsors/xteam-logo.png" width="200" alt="x-team">
<br />
<b>Work from anywhere in the world with top tech companies like Riot Games, Coinbase, and Google.</b>
</a>
</p>
<p>
<a href="https://m.do.co/c/bd3b723c0a36?utm_medium=opensource&amp;utm_source=awesome-go"><img src="https://avelino.run/sponsors/do_logo_horizontal_blue-210.png" alt="Digital Ocean"></a>
</p>
</div>
<i><a href="/#contents" alt="back to content menu" title="back to content menu">🗺️ back to content menu</a></i>
<ul>
{{range .Items}}
<li><a href="{{.Url}}?utm_campaign=awesomego&utm_medium=referral&utm_source=awesomego" alt="Go to {{.Title}} link" title="Go to {{.Title}} link">{{.Description}}</a></li>
{{end}}
</ul>
<a href="https://bit.ly/awesome-go-netlify">
<img src="https://www.netlify.com/img/global/badges/netlify-dark.svg" alt="Deploys by Netlify" />
</a>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/1.1.0/marked.min.js" integrity="sha512-uggp1jOpxGjqTeS8Fit5x6+lqyJoIuXXn/VziVPlxBRnqZ0FhCaxsUnQsPL5PKylHr0KIoMtNbBIiU6n31dDTg==" crossorigin="anonymous"></script>
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-85465107-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

113
tmpl/category-index.tmpl.html vendored Normal file
View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>{{.Title}} - Awesome Go / Golang</title>
<meta name="description" content=
"{{.Description}} - Awesome Go / Golang">
<meta name="keywords" content=
"{{.Title}}, golang, go, awesome, awesome-go, go framework, golang framework">
<meta name="twitter:card" value="summary">
<meta property="og:title" content=
"{{.Description}} - Awesome Go">
<meta property="og:type" content="article">
<meta property="og:url" content=
"https://awesome-go.com/{{.Slug}}">
<meta property="og:image" content=
"https://awesome-go.com/assets/logo.png">
<meta property="og:description" content=
"{{.Description}} - Awesome Go">
<link rel="canonical" href="https://awesome-go.com/{{.Slug}}">
<link rel="stylesheet" type="text/css" href=
"/assets/fonts/firasans.css">
<link rel="stylesheet" type="text/css" href=
"/assets/normalize.css">
<link rel="stylesheet" type="text/css" href=
"/assets/awesome-go.css"><!--ICONS-->
<link rel="icon" href="/assets/favicon/favicon.ico" type=
"image/x-icon">
<link rel="apple-touch-icon" href=
"/assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="/assets/favicon/manifest.json">
</head>
<body>
<div id="content">
<header>
<h1><a href="https://awesome-go.com/"><img align=
"right" src=
"https://github.com/avelino/awesome-go/raw/main/tmpl/assets/logo.png"
alt="awesome-go" title="awesome-go"></a> {{.Title}} -
<a href="https://awesome-go.com/">Awesome Go</a></h1>
<p><em>{{.Description}}</em></p>
<p><a href=
"https://github.com/avelino/awesome-go/actions/workflows/tests.yaml?query=branch%3Amain"
rel="nofollow"><img src=
"https://github.com/avelino/awesome-go/actions/workflows/tests.yaml/badge.svg?branch=main"
alt="Build Status"></a> <a href=
"https://github.com/sindresorhus/awesome" rel=
"nofollow"><img src=
"https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"
alt="Awesome"></a> <a href=
"https://gophers.slack.com/messages/awesome" rel=
"nofollow"><img src=
"https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&amp;logo=slack&amp;colorB=red"
alt="Slack Widget"></a> <a href=
"https://app.netlify.com/sites/awesome-go/deploys" rel=
"nofollow"><img src=
"https://api.netlify.com/api/v1/badges/83a6dcbe-0da6-433e-b586-f68109286bd5/deploy-status"
alt="Netlify Status"></a> <a href=
"https://www.trackawesomelist.com/avelino/awesome-go/"
rel="nofollow"><img src=
"https://www.trackawesomelist.com/badge.svg" alt=
"Track Awesome List"></a></p>
<p><a href=
"https://www.producthunt.com/posts/awesome-go?utm_source=badge-featured&amp;utm_medium=badge&amp;utm_souce=badge-awesome-go"
rel="nofollow"><img src=
"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291535&amp;theme=light"
width="250" height="54"></a></p>
</header><i><a href="/#contents" alt="back to content menu"
title="back to content menu">🗺️ back to content
menu</a></i>
<ul>
{{range .Links}}
<li>
<a href=
"{{.URL}}?utm_campaign=awesomego&amp;utm_medium=referral&amp;utm_source=awesomego"
alt="Go to {{.Title}} link" title=
"Go to {{.Title}} link">{{.Description}}</a>
</li>{{end}}
</ul><a href="https://bit.ly/awesome-go-netlify"><img src=
"https://www.netlify.com/img/global/badges/netlify-dark.svg"
alt="Deploys by Netlify"></a>
</div>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
integrity=
"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
crossorigin="anonymous"></script>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/marked/1.1.0/marked.min.js"
integrity=
"sha512-uggp1jOpxGjqTeS8Fit5x6+lqyJoIuXXn/VziVPlxBRnqZ0FhCaxsUnQsPL5PKylHr0KIoMtNbBIiU6n31dDTg=="
crossorigin="anonymous"></script>
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-85465107-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

View File

@ -1,40 +1,39 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Language" content="en"> <meta name="viewport" content="width=device-width" />
<meta name="viewport" content="width=device-width">
<title>A curated list of awesome Go frameworks, libraries and software - Awesome Go / Golang</title> <title>A curated list of awesome Go frameworks, libraries and software - Awesome Go / Golang</title>
<meta name="description" content="A curated list of awesome Go / Golang frameworks, libraries and software"> <meta name="description" content="A curated list of awesome Go / Golang frameworks, libraries and software" />
<meta name="keywords" content="golang, go, awesome, awesome-go, go framework, golang framework"> <meta name="keywords" content="golang, go, awesome, awesome-go, go framework, golang framework" />
<meta name="twitter:card" value="summary"> <meta name="twitter:card" value="summary" />
<meta property="og:title" content="A curated list of awesome Go frameworks, libraries and software - Awesome Go" /> <meta property="og:title" content="A curated list of awesome Go frameworks, libraries and software - Awesome Go" />
<meta property="og:type" content="article" /> <meta property="og:type" content="article" />
<meta property="og:url" content="https://awesome-go.com/" /> <meta property="og:url" content="https://awesome-go.com/" />
<meta property="og:image" content="https://awesome-go.com/assets/logo.png" /> <meta property="og:image" content="https://awesome-go.com/assets/logo.png" />
<meta property="og:description" content="A curated list of awesome #Golang frameworks, libraries and software" /> <meta property="og:description" content="A curated list of awesome #Golang frameworks, libraries and software" />
<link rel="canonical" href="https://awesome-go.com/"> <link rel="canonical" href="https://awesome-go.com/" />
<link rel="stylesheet" type="text/css" href="/assets/fonts/firasans.css"> <link rel="stylesheet" type="text/css" href="/assets/fonts/firasans.css" />
<link rel="stylesheet" type="text/css" href="/assets/normalize.css"> <link rel="stylesheet" type="text/css" href="/assets/normalize.css" />
<link rel="stylesheet" type="text/css" href="/assets/awesome-go.css"> <link rel="stylesheet" type="text/css" href="/assets/awesome-go.css" />
<!--ICONS--> <!--ICONS-->
<link rel="icon" href="./assets/favicon/favicon.ico" type="image/x-icon"> <link rel="icon" href="/assets/favicon/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="./assets/favicon/apple-touch-icon.png"> <link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png" />
<link rel="manifest" href="./assets/favicon/manifest.json"> <link rel="manifest" href="/assets/favicon/manifest.json" />
</head> </head>
<body> <body>
<div id="content"> <div id="content">
<p> <div>
<div id="amzn-assoc-ad-ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></div> <div id="amzn-assoc-ad-ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></div>
<script async src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US&adInstanceId=ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></script> <script async src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US&adInstanceId=ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></script>
</p> </div>
{{.Body}} {{.Body}}