Merge branch 'main' into main

This commit is contained in:
Phani Rithvij 2024-08-15 11:27:03 +05:30 committed by GitHub
commit f96737cbb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 1643 additions and 1251 deletions

View File

@ -18,5 +18,3 @@ engines:
ratings:
paths:
- "**.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/), [gocover](http://gocover.io/) etc.):
_Not every repository (project) will require every option, but most projects should. Check the Contribution Guidelines for details._
**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 detials._
- [ ] 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 coverage service link.
- [ ] The repo documentation has a goreportcard link.
- [ ] 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 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

@ -3,16 +3,20 @@ on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
name: Running test
runs-on: ubuntu-latest
container: golang:latest
steps:
- uses: actions/checkout@1.0.0
- uses: actions/checkout@v4
- name: Get dependencies
run: go get -v -t -d ./...
- name: run script
run: go test stale_repositories_test.go scripts.go
run: go test -v -run ^TestStaleRepository$
env:
OAUTH_TOKEN: ${{secrets.OAUTH_TOKEN}}

View File

@ -5,6 +5,9 @@ on:
branches:
- 'main'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
name: Make and Deploy site
@ -12,15 +15,15 @@ jobs:
environment: netlify
container: golang:latest
steps:
- uses: actions/checkout@1.0.0
- uses: actions/checkout@v4
- name: Get dependencies
run: go get -v -t -d ./...
- name: Make awesome-go.com
run: go run make_site.go scripts.go
run: go run .
- name: deploy awesome-go.com
uses: jsmrcaga/action-netlify-deploy@v1.1.0
with:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_DEPLOY_TO_PROD: true
build_directory: tmpl
build_directory: out

View File

@ -1,22 +0,0 @@
name: PR auto close, if you stay more than 8 days open
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: >
This PR has been automatically marked as stale because it has not had
recent activity. They will wait 15 days for your interaction, after
that the PR will be closed.
Please read more in https://github.com/avelino/awesome-go/blob/master/CONTRIBUTING.md
stale-pr-label: 'stale'
days-before-stale: 15
days-before-close: 7

View File

@ -6,14 +6,17 @@ on:
- 'main'
pull_request:
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
name: Running test
runs-on: ubuntu-latest
container: golang:latest
steps:
- uses: actions/checkout@1.0.0
- uses: actions/checkout@v4
- name: Get dependencies
run: go get -v -t -d ./...
- 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
vendor
# Folders
.idea
@ -9,5 +8,3 @@ test_stale_repositories_log
*.exe
# Local Netlify folder
.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.
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.
@ -24,7 +24,7 @@ The following behaviors are expected and requested of all community members:
* Exercise consideration and respect in your speech and actions.
* Attempt collaboration before conflict.
* 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.
## 4. 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.
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
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)
@ -73,7 +73,7 @@ This code of conduct and its related procedures also applies to unacceptable beh
## 9. Contact info
t@avelino.xxx
avelinorun AT gmail DOT com
## 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).
Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
Retrieved on November 22, 2016

View File

@ -1,22 +1,24 @@
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).
# Contribution Guidelines
> 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**.
> 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**.
- **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),
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.
> 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
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.
(https://goreportcard.com/report/github.com/ **github_user** / **github_repo**):
- have an **open source license**, [see list of allowed licenses](https://opensource.org/licenses/alphabetical);
@ -26,12 +28,13 @@ To be on the list, project repositories should adhere to the following quality s
- regular, recent commits;
- or, for finished projects, issues and pull requests are responded to generally within 2 weeks;
- be stable or progressing toward stable;
- be thoroughly documented (README, pkg.go.dev doc comments, etc.) in the english language, so everyone is able to understand the project's intention and how it works. All public functions and types should have a Go style documentation header;
- if the library/program is testable, then coverage should be >= 80% for non-data-related packages and >=90% for data related packages. (**Note**: the tests will be reviewed too. We will check your coverage manually if your package's coverage is just a benchmark results);
- have at least one official version-numbered release that allows go.mod files to list the file by version number, of the form vX.X.X.
- be thoroughly documented (README, pkg.go.dev doc comments, etc.) in the English language, so everyone is able to understand the project's intention and how it works. All public functions and types should have a Go-style documentation header;
- if the library/program is testable, then coverage should be >= 80% for non-data-related packages and >=90% for data-related packages. (**Note**: the tests will be reviewed too. We will check your coverage manually if your package's coverage is just a benchmark result);
- have at least one official version-numbered release that allows go.mod files to list the file by version number of the form vX.X.X.
Categories must have at least 3 items.
## Preparing for review
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.
- 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.
## How to add an item to the list
Open a pull request against the README.md document that adds the repository to the list.
@ -54,17 +58,17 @@ Open a pull request against the README.md document that adds the repository to t
- The added item should be in alphabetical order within its category.
- The link should be the name of the package or project.
- Descriptions should be clear, concise, and non-promotional.
- Descriptions should follow the link, on the same line and end with a punctuation mark.
- Remember to put a period `.` at end of the project description.
- Descriptions should follow the link on the same line and end with a punctuation mark.
- 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
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).
## 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-flat.svg)](https://github.com/avelino/awesome-go)
@ -72,43 +76,47 @@ 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-flat.svg)](https://github.com/avelino/awesome-go)
```
## Maintenance expectations for projects listed here
To prevent removal from awesome-go, your project must maintain the following quality standards.
- Development should be on-going and maintain code quality. Official releases should be at least once a year if the project is ongoing.
- Development should be ongoing and maintain code quality. Official releases should be at least once a year if the project is ongoing.
- Or, if development has halted because the project is mature and stable, that can be demonstrated by having no bug reports in the Issues list that are older than 6 months.
- All links to quality reports should be to the most recent official release or current ongoing development.
Highly recommended but not required:
- A continuous integration process 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 pull-request process requires the continuous-integration tests pass before a pull request can be merged
- 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 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
- Open a pull request that deletes the line of the project in question.
- Delete the submission template and substitute a description of which criteria the project is not meeting. It should be a combination of the following.
- The project has not made an official release within the last year and it has open issues.
- The project has not made an official release within the last year and has open issues.
- The project is not responding to bug reports issued within 6 months of submission.
- The project is not meeting quality standards as indicated by the Go Report Card or Code Coverage tests.
- The quality standard links have been removed from the documentation.
- The project is no longer open-sourced.
- The project is not compatible with any Go version issued within the last year (there is hopefully an open PR about this at the project).
- The project is incompatible with any Go version issued within the last year (there is hopefully an open PR about this at the project).
If the project is hosted on Github, include a link to the project's submitter and/or author so
If the project is hosted on GitHub, include a link to the project's submitter and/or author so
that they will be notified of the desire to remove the project and have an opportunity to respond.
The link should be of the form @githubID.
If the project is not hosted on Github, open an issue at the project in question's repository linking to the PR
If the project is not hosted on GitHub, open an issue at the project in question's repository linking to the PR
and stating the following:
>This project is currently listed at awesome-go at https://github.com/avelino/awesome-go.
However, it appears that the project is not maintaining the quality standards required to continue to be listed at the awesome-go project.
This project is schedule to be removed within 2 weeks of this posting. To continue to be listed at awesome-go, please respond at:
This project is scheduled to be removed within 2 weeks of this posting. To continue to be listed at awesome-go, please respond at:
-- link to above PR --
Then, comment on your PR at awesome-go with a link to the removal issue at the project.
## 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.
@ -120,12 +128,41 @@ that the PR will be closed.
## 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.
## 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
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.
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.
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.
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.
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
name: Go # The name of the workflow that will appear on Github

756
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
go 1.17
go 1.21
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/yuin/goldmark v1.4.13
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
github.com/otiai10/copy v1.14.0
github.com/yuin/goldmark v1.6.0
golang.org/x/oauth2 v0.15.0
)
require (
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/golang/protobuf v1.4.2 // indirect
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.25.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.18.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=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
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/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/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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
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=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
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/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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-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/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.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.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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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-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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
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=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=

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
import (
"io/ioutil"
"bytes"
"github.com/avelino/awesome-go/pkg/markdown"
"os"
"regexp"
"sort"
"strings"
@ -16,9 +18,38 @@ var (
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) {
query := startQuery()
query.Find("body > ul").Each(func(i int, s *goquery.Selection) {
doc := goqueryFromReadme(t)
doc.Find("body > ul").Each(func(i int, s *goquery.Selection) {
if i != 0 {
// skip content menu
// 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) {
query := startQuery()
doc := goqueryFromReadme(t)
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) {
href, ok := s.Attr("href")
if !ok {
@ -49,10 +80,9 @@ func TestDuplicatedLinks(t *testing.T) {
// Test if an entry has description, it must be separated from link with ` - `
func TestSeparator(t *testing.T) {
var matched, containsLink, noDescription bool
input, err := ioutil.ReadFile("./README.md")
if err != nil {
panic(err)
}
input, err := os.ReadFile(readmePath)
requireNoErr(t, err, "readme should be exists")
lines := strings.Split(string(input), "\n")
for _, line := range lines {
line = strings.Trim(line, " ")
@ -69,11 +99,12 @@ func TestSeparator(t *testing.T) {
}
}
}
func TestGenerateHTML(t *testing.T) {
err := GenerateHTML()
if err != nil {
t.Errorf("html generate error '%s'", err.Error())
}
func TestRenderIndex(t *testing.T) {
requireNoErr(t, mkdirAll(outDir), "output dir should exists")
err := renderIndex(readmePath, outIndexFile)
requireNoErr(t, err, "html should be rendered")
}
func testList(t *testing.T, list *goquery.Selection) {

View File

@ -1,115 +0,0 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"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() {
GenerateHTML()
input, err := ioutil.ReadFile("./tmpl/index.html")
if err != nil {
panic(err)
}
buf := bytes.NewBuffer(input)
query, err := goquery.NewDocumentFromReader(buf)
if err != nil {
panic(err)
}
objs := []Object{}
query.Find("body #content ul ul").First().Each(func(_ int, s *goquery.Selection) {
s.Find("li a").Each(func(_ int, s *goquery.Selection) {
selector, _ := s.Attr("href")
obj := makeObjById(selector, query.Find("body"))
objs = append(objs, obj)
})
})
makeSiteStruct(objs)
makeSitemap(objs)
changeLinksInIndex(string(input), query)
}
func makeSiteStruct(objs []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 []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 := desc.NextFiltered("ul")
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)
})
obj = Object{
Slug: slug.Generate(s.Text()),
Title: s.Text(),
Description: desc.Text(),
Items: links,
}
})
return
}
func changeLinksInIndex(html string, query *goquery.Document) {
query.Find("body #content ul li ul li a").Each(func(_ int, s *goquery.Selection) {
href, exists := s.Attr("href")
if exists {
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"
)
// ConvertMarkdownToHTML converts markdown byte slice to a HTML byte slice
func ConvertMarkdownToHTML(markdown []byte) ([]byte, error) {
// ToHTML converts markdown byte slice to a HTML byte slice
func ToHTML(markdown []byte) ([]byte, error) {
md := goldmark.New(
goldmark.WithExtensions(extension.GFM),
goldmark.WithParserOptions(

View File

@ -5,7 +5,7 @@ import (
"testing"
)
func TestConvertMarkdownToHTML(t *testing.T) {
func TestToHTML(t *testing.T) {
input := []byte(
`## some headline
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>`,
)
got, err := ConvertMarkdownToHTML(input)
got, err := ToHTML(input)
if err != nil {
t.Errorf("ConvertMarkdownToHTML() error = %v", err)
t.Errorf("ToHTML() error = %v", err)
return
}
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
func Generate(text string) string {
// FIXME: this is should be like regexp.Replace(`[^-a-zA-Z\d]+`, ``)
s := strings.ReplaceAll(text, "/", "")
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"
)
const issueTemplate = `
const issueTemplateContent = `
{{range .}}
- [ ] {{.}}
{{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 githubGETREPO = "https://api.github.com/repos%s"
var githubGETCOMMITS = "https://api.github.com/repos%s/commits"
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
// 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 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 movedPermanently = " status code 301 received"
const status302 = " status code 302 received"
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 ctr = 0
type tokenSource struct {
AccessToken string
}
type issue struct {
Title string `json:"title"`
Body string `json:"body"`
}
type repo struct {
Archived bool `json:"archived"`
}
func (t *tokenSource) Token() (*oauth2.Token, error) {
token := &oauth2.Token{
return &oauth2.Token{
AccessToken: t.AccessToken,
}
return token, nil
}, nil
}
func getRepositoriesFromBody(body string) []string {
links := strings.Split(body, "- ")
for idx, link := range links {
str := strings.ReplaceAll(link, "\r", "")
str = strings.ReplaceAll(str, "[ ]", "")
str = strings.ReplaceAll(str, "[x]", "")
str = strings.ReplaceAll(str, " ", "")
str = strings.ReplaceAll(str, "\n", "")
str = strings.ReplaceAll(str, deadLinkMessage, "")
str = strings.ReplaceAll(str, movedPermanently, "")
str = strings.ReplaceAll(str, status302, "")
str = strings.ReplaceAll(str, archived, "")
links[idx] = str
for i, link := range links {
link = strings.ReplaceAll(link, "\r", "")
link = strings.ReplaceAll(link, "[ ]", "")
link = strings.ReplaceAll(link, "[x]", "")
link = strings.ReplaceAll(link, " ", "")
link = strings.ReplaceAll(link, "\n", "")
link = strings.ReplaceAll(link, deadLinkMessage, "")
link = strings.ReplaceAll(link, movedPermanently, "")
link = strings.ReplaceAll(link, status302, "")
link = strings.ReplaceAll(link, archived, "")
links[i] = link
}
return links
}
func generateIssueBody(repositories []string) (string, error) {
var writer bytes.Buffer
t := template.New("issue")
temp, err := t.Parse(issueTemplate)
if err != nil {
log.Print("Failed to generate template")
return "", err
}
err = temp.Execute(&writer, repositories)
if err != nil {
log.Print("Failed to generate template")
return "", err
}
issueBody := writer.String()
return issueBody, nil
func generateIssueBody(t *testing.T, repositories []string) (string, error) {
t.Helper()
buf := bytes.NewBuffer(nil)
err := issueTemplate.Execute(buf, repositories)
requireNoErr(t, err, "Failed to generate template")
return buf.String(), nil
}
func createIssue(staleRepos []string, client *http.Client) {
func createIssue(t *testing.T, staleRepos []string, client *http.Client) {
t.Helper()
if len(staleRepos) == 0 {
log.Print("NO STALE REPOSITORIES")
return
}
body, err := generateIssueBody(staleRepos)
if err != nil {
log.Print("Failed at CreateIssue")
return
}
body, err := generateIssueBody(t, staleRepos)
requireNoErr(t, err, "failed to generate issue body")
newIssue := &issue{
Title: issueTitle,
Body: body,
}
buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(newIssue)
req, err := http.NewRequest("POST", githubPOSTISSUES, buf)
if err != nil {
log.Print("Failed at CreateIssue")
return
}
client.Do(req)
buf := bytes.NewBuffer(nil)
requireNoErr(t, json.NewEncoder(buf).Encode(newIssue), "failed to encode json req")
req, err := http.NewRequest(http.MethodPost, githubPOSTISSUES, buf)
requireNoErr(t, err, "failed to create request")
_, roundTripErr := 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)
if err != nil {
log.Print("Failed to get all issues")
return err
}
func getAllFlaggedRepositories(t *testing.T, client *http.Client) map[string]bool {
t.Helper()
req, err := http.NewRequest(http.MethodGet, awesomeGoGETISSUES, nil)
requireNoErr(t, err, "failed to create request")
res, err := client.Do(req)
if err != nil {
log.Print("Failed to get all issues")
return err
}
target := []issue{}
requireNoErr(t, err, "failed to send request")
defer res.Body.Close()
json.NewDecoder(res.Body).Decode(&target)
for _, i := range target {
if i.Title == issueTitle {
repos := getRepositoriesFromBody(i.Body)
var issues []issue
requireNoErr(t, json.NewDecoder(res.Body).Decode(&issues), "failed to unmarshal response")
addressedRepositories := make(map[string]bool)
for _, issue := range issues {
if issue.Title != issueTitle {
continue
}
repos := getRepositoriesFromBody(issue.Body)
for _, repo := range repos {
(*flaggedRepositories)[repo] = true
addressedRepositories[repo] = true
}
}
}
return nil
return addressedRepositories
}
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 {
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("GET", apiCall, nil)
var repoResp repo
isRepoAdded := false
req, err := http.NewRequest(http.MethodGet, apiCall, nil)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
return nil, false
}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
return nil, false
}
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&repoResp)
if resp.StatusCode == 301 {
*staleRepos = append(*staleRepos, href+movedPermanently)
log.Printf("%s returned 301", href)
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 == 302 && !isRepoAdded {
*staleRepos = append(*staleRepos, href+status302)
log.Printf("%s returned 302", href)
if resp.StatusCode == http.StatusFound && !isRepoAdded {
warnings = append(warnings, href+status302)
log.Printf("%s returned %d", href, resp.StatusCode)
isRepoAdded = true
}
if resp.StatusCode >= 400 && !isRepoAdded {
*staleRepos = append(*staleRepos, href+deadLinkMessage)
if resp.StatusCode >= http.StatusBadRequest && !isRepoAdded {
warnings = append(warnings, href+deadLinkMessage)
log.Printf("%s might not exist!", href)
isRepoAdded = true
}
if repoResp.Archived && !isRepoAdded {
*staleRepos = append(*staleRepos, href+archived)
warnings = append(warnings, href+archived)
log.Printf("%s is archived!", href)
isRepoAdded = true
}
return isRepoAdded
}
return false
// FIXME: expression `(len(warnings) > 0) == isRepoAdded` is always true.
return warnings, isRepoAdded
}
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)
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("GET", apiCall, nil)
isRepoAdded := false
req, err := http.NewRequest(http.MethodGet, apiCall, nil)
if err != nil {
log.Printf("Failed at repository %s\n", href)
return false
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 false
return nil, false
}
defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&respObj)
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)
*staleRepos = append(*staleRepos, href)
warnings = append(warnings, href)
isRepoAdded = true
}
return isRepoAdded
}
return false
// FIXME: expression `(len(warnings) > 0) == isRepoAdded` is always true.
return warnings, isRepoAdded
}
func testStaleRepository() {
query := startQuery()
var staleRepos []string
addressedRepositories := make(map[string]bool)
func TestStaleRepository(t *testing.T) {
doc := goqueryFromReadme(t)
oauth := os.Getenv("OAUTH_TOKEN")
client := &http.Client{}
client := &http.Client{
Transport: &http.Transport{},
}
if oauth == "" {
log.Print("No oauth token found. Using unauthenticated client ...")
} else {
@ -231,42 +270,47 @@ func testStaleRepository() {
}
client = oauth2.NewClient(context.Background(), tokenSource)
}
err := getAllFlaggedRepositories(client, &addressedRepositories)
if err != nil {
log.Println("Failed to get existing issues. Exiting...")
return
}
query.Find("body li > a:first-child").EachWithBreak(func(_ int, s *goquery.Selection) bool {
// FIXME: return addressedRepositories, no need to pass
addressedRepositories := getAllFlaggedRepositories(t, client)
var staleRepos []string
doc.
Find("body li > a:first-child").
EachWithBreak(func(_ int, s *goquery.Selection) bool {
href, ok := s.Attr("href")
if !ok {
log.Println("expected to have href")
return true
}
if ctr >= LIMIT && LIMIT != -1 {
log.Print("Max number of issues created")
return false
}
issueExists := containsOpenIssue(href, addressedRepositories)
if issueExists {
if _, issueExists := addressedRepositories[href]; issueExists {
log.Printf("issue already exists for %s\n", href)
} else {
isGithubRepo := reGithubRepo.MatchString(href)
if isGithubRepo {
isRepoAdded := testRepoState(true, href, client, &staleRepos)
isRepoAdded = testCommitAge(!isRepoAdded, href, client, &staleRepos)
return true
}
if !reGithubRepo.MatchString(href) {
log.Printf("%s non-github repo not currently handled", href)
}
// FIXME: this is `or` expres24sion. Probably we need `and`?
warnings, isRepoAdded := checkRepoAvailability(true, href, client)
staleRepos = append(staleRepos, warnings...)
warnings, isRepoAdded = checkRepoCommitActivity(!isRepoAdded, href, client)
staleRepos = append(staleRepos, warnings...)
if isRepoAdded {
ctr++
}
} else {
log.Printf("%s non-github repo not currently handled", href)
}
}
return true
})
createIssue(staleRepos, client)
}
func TestStaleRepository(t *testing.T) {
testStaleRepository()
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;
line-height: 1;
}
td {
padding: 6px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,19 @@
{
"name": "Awesome Go",
"short_name": "Awesome-Go",
"icons": [
{
"src": "./android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

109
tmpl/cat-tmpl.html vendored
View File

@ -1,109 +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">
</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,34 +1,39 @@
<!DOCTYPE html>
<html>
<html lang="en">
<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">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<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="keywords" content="golang, go, awesome, awesome-go, go framework, golang framework">
<meta name="twitter:card" value="summary">
<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="twitter:card" value="summary" />
<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:url" content="https://awesome-go.com/" />
<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" />
<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/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" />
<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">
</head>
<body>
<div id="content">
<p>
<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>
</p>
</div>
{{.Body}}

View File

@ -1 +1,2 @@
User-Agent: *
Allow: /