The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by
removing the max tokens filter.
├── .codeclimate.yml
├── .dockerignore
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ └── config.yml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── check-for-spammy-issues.yml
│ ├── pull-request-first-comment.yaml
│ ├── run-check.yaml
│ ├── site-deploy.yaml
│ └── tests.yaml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── COVERAGE.md
├── LICENSE
├── MAINTAINERS
├── README.md
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── netlify.toml
├── pkg
├── markdown
│ ├── convert.go
│ └── convert_test.go
└── slug
│ ├── generator.go
│ └── generator_test.go
├── stale_repositories_test.go
└── tmpl
├── assets
├── awesome-go.css
├── favicon
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ └── manifest.json
├── fonts
│ ├── firasans.css
│ ├── firasans.ttf
│ ├── firasans.woff
│ ├── firasans.woff2
│ ├── firasansbold.ttf
│ ├── firasansbold.woff
│ ├── firasansbold.woff2
│ ├── firasansbolditalic.ttf
│ ├── firasansbolditalic.woff
│ ├── firasansbolditalic.woff2
│ ├── firasansbook.ttf
│ ├── firasansbook.woff
│ ├── firasansbook.woff2
│ ├── firasansbookitalic.ttf
│ ├── firasansbookitalic.woff
│ ├── firasansbookitalic.woff2
│ ├── firasansextralight.ttf
│ ├── firasansextralight.woff
│ ├── firasansextralight.woff2
│ ├── firasansextralightitalic.ttf
│ ├── firasansextralightitalic.woff
│ ├── firasansextralightitalic.woff2
│ ├── firasansitalic.ttf
│ ├── firasansitalic.woff
│ ├── firasansitalic.woff2
│ ├── firasanslight.ttf
│ ├── firasanslight.woff
│ ├── firasanslight.woff2
│ ├── firasanslightitalic.ttf
│ ├── firasanslightitalic.woff
│ ├── firasanslightitalic.woff2
│ ├── firasansmedium.ttf
│ ├── firasansmedium.woff
│ ├── firasansmedium.woff2
│ ├── firasansmediumitalic.ttf
│ ├── firasansmediumitalic.woff
│ ├── firasansmediumitalic.woff2
│ ├── firasanssemibold.ttf
│ ├── firasanssemibold.woff
│ ├── firasanssemibold.woff2
│ ├── firasanssemibolditalic.ttf
│ ├── firasanssemibolditalic.woff
│ └── firasanssemibolditalic.woff2
├── logo.png
├── normalize.css
└── sponsors
│ └── doppler.png
├── category-index.tmpl.html
├── index.tmpl.html
├── robots.txt
└── sitemap.tmpl.xml
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | engines:
3 | rubocop:
4 | enabled: true
5 | golint:
6 | enabled: true
7 | gofmt:
8 | enabled: true
9 | govet:
10 | enabled: true
11 | fixme:
12 | enabled: true
13 | duplication:
14 | enabled: true
15 | config:
16 | languages:
17 | - go
18 | ratings:
19 | paths:
20 | - "**.go"
21 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | tmpl/assets/* linguist-vendored
2 | *.js linguist-vendored
3 | *.css linguist-vendored
4 | *.html linguist-vendored
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: avelino
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Report a bug encountered
3 | labels: ["bug", "pending-review"]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Thank you very much for opening a bug report at awesome-go.
9 |
10 | If you have a feature idea or need help, please go to [our Forum](https://github.com/avelino/awesome-go/discussions).
11 | 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.
12 | - type: checkboxes
13 | id: confirm-search
14 | attributes:
15 | label: Search first
16 | 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.
17 | options:
18 | - label: I searched and no similar issues were found
19 | required: true
20 | - type: textarea
21 | id: problem
22 | attributes:
23 | label: What Happened?
24 | description: |
25 | Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner.
26 | validations:
27 | required: true
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | contact_links:
2 | - name: Feature request
3 | url: https://github.com/avelino/awesome-go/discussions/new?category=ideas
4 | about: Suggest an idea for awesome-go
5 | - name: Questions & Help
6 | url: https://github.com/avelino/awesome-go/discussions/new?category=q-a
7 | about: Ask a question about awesome-go
8 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## We want to ensure high quality of the packages. Make sure that you've checked the boxes below before sending a pull request.
2 |
3 | - [ ] I have read the [Contribution Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines)
4 | - [ ] I have read the [Maintainers Note](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#maintainers)
5 | - [ ] I have read the [Quality Standards](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards)
6 |
7 | _Not every repository (project) will require every option, but most projects should. Check the Contribution Guidelines for details._
8 |
9 | - [ ] The repo documentation has a pkg.go.dev link.
10 | - [ ] The repo documentation has a coverage service link.
11 | - [ ] The repo documentation has a goreportcard link.
12 | - [ ] The repo has a version-numbered release and a go.mod file.
13 | - [ ] The repo has a continuous integration process that automatically runs tests that must pass before new pull requests are merged.
14 | - [ ] Continuous integration is used to attempt to catch issues prior to releasing this package to end-users.
15 |
16 | ## Please provide some links to your package to ease the review
17 |
18 | - [ ] forge link (github.com, gitlab.com, etc):
19 | - [ ] pkg.go.dev:
20 | - [ ] goreportcard.com:
21 | - [ ] coverage service link ([codecov](https://codecov.io/), [coveralls](https://coveralls.io/), etc.):
22 |
23 | ## Pull Request content
24 |
25 | - [ ] The package has been added to the list in alphabetical order.
26 | - [ ] The package has an appropriate description with correct grammar.
27 | - [ ] As far as I know, the package has not been listed here before.
28 |
29 | ## Category quality
30 |
31 | _Note that new categories can be added only when there are 3 packages or more._
32 |
33 | 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.
34 |
35 | Please delete one of the following lines:
36 |
37 | - [ ] The packages around my addition still meet the Quality Standards.
38 | - [ ] I removed the following packages around my addition: (please give a short reason for each removal)
39 |
40 | Thanks for your PR, you're awesome! :sunglasses:
41 |
--------------------------------------------------------------------------------
/.github/workflows/check-for-spammy-issues.yml:
--------------------------------------------------------------------------------
1 | name: Issues spammy check
2 | on:
3 | issues:
4 | types: [opened]
5 |
6 | jobs:
7 | mark-as-spam:
8 | name: Remove issues with spammy
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: close issue
12 | uses: balevine/mark-as-spam@v1.0
13 | env:
14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request-first-comment.yaml:
--------------------------------------------------------------------------------
1 | name: First comment in new pull request
2 |
3 | on:
4 | pull_request_target:
5 | types: [opened]
6 |
7 | jobs:
8 | commentCreated:
9 | runs-on: ubuntu-latest
10 | permissions:
11 | pull-requests: write
12 | issues: write
13 | environment: action
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 | steps:
17 | - name: first comment
18 | uses: peter-evans/create-or-update-comment@v3
19 | with:
20 | token: ${{ secrets.GITHUB_TOKEN }}
21 | issue-number: ${{ github.event.pull_request.number }}
22 | body: |
23 | Thank you for contributing to [awesome-go](https://awesome-go.com/). We will review your contribution as soon as possible.
24 |
25 | 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):
26 | - repo link
27 | - pkg.go.dev
28 | - goreportcard.com
29 | - coverage
30 |
31 | > Your project is under review. It may take a few days to be approved.
32 |
--------------------------------------------------------------------------------
/.github/workflows/run-check.yaml:
--------------------------------------------------------------------------------
1 | name: Check For Stale Repositories
2 | on:
3 | workflow_dispatch:
4 | schedule:
5 | - cron: '0 0 * * 0'
6 |
7 | permissions:
8 | contents: read # to fetch code (actions/checkout)
9 |
10 | jobs:
11 | build:
12 | name: Running test
13 | runs-on: ubuntu-latest
14 | container: golang:latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Get dependencies
18 | run: go get -v -t -d ./...
19 | - name: run script
20 | run: go test -v -run ^TestStaleRepository$
21 | env:
22 | OAUTH_TOKEN: ${{secrets.OAUTH_TOKEN}}
23 |
--------------------------------------------------------------------------------
/.github/workflows/site-deploy.yaml:
--------------------------------------------------------------------------------
1 | name: site-deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 |
8 | permissions:
9 | contents: read # to fetch code (actions/checkout)
10 |
11 | jobs:
12 | build:
13 | name: Make and Deploy site
14 | runs-on: ubuntu-latest
15 | environment: netlify
16 | container: golang:latest
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Get dependencies
20 | run: go get -v -t -d ./...
21 | - name: Make awesome-go.com
22 | run: go run .
23 | - name: Setup node
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version: 20
27 | - name: deploy awesome-go.com
28 | uses: nwtgck/actions-netlify@v3.0
29 | with:
30 | publish-dir: "./out"
31 | production-branch: main
32 | production-deploy: true
33 | enable-pull-request-comment: false
34 | enable-commit-comment: false
35 | enable-commit-status: false
36 | overwrites-pull-request-comment: false
37 | env:
38 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
39 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
40 | timeout-minutes: 1
41 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 |
9 | permissions:
10 | contents: read # to fetch code (actions/checkout)
11 |
12 | jobs:
13 | build:
14 | name: Running test
15 | runs-on: ubuntu-latest
16 | container: golang:latest
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Get dependencies
20 | run: go get -v -t -d ./...
21 | - name: Run tests
22 | run: go test main_test.go main.go
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out/
2 | awesome-go
3 |
4 | # Folders
5 | .idea
6 | .vscode
7 | test_stale_repositories_log
8 | *.exe
9 | # Local Netlify folder
10 | .netlify
11 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## 1. Purpose
4 |
5 | A primary goal of Awesome Go is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
8 |
9 | We invite all those who participate in Awesome Go to help us create safe and positive experiences for everyone.
10 |
11 | ## 2. Open Source Citizenship
12 |
13 | 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.
14 |
15 | 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.
16 |
17 | 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.
18 |
19 | ## 3. Expected Behavior
20 |
21 | The following behaviors are expected and requested of all community members:
22 |
23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
24 | * Exercise consideration and respect in your speech and actions.
25 | * Attempt collaboration before conflict.
26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech.
27 | * 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.
28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
29 |
30 | ## 4. Unacceptable Behavior
31 |
32 | The following behaviors are considered harassment and are unacceptable within our community:
33 |
34 | * Violence, threats of violence or violent language directed against another person.
35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
36 | * Posting or displaying sexually explicit or violent material.
37 | * Posting or threatening to post other people’s personally identifying information ("doxing").
38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
39 | * Inappropriate photography or recording.
40 | * Inappropriate physical contact. You should have someone’s consent before touching them.
41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
42 | * Deliberate intimidation, stalking or following (online or in person).
43 | * Advocating for, or encouraging, any of the above behavior.
44 | * Sustained disruption of community events, including talks and presentations.
45 |
46 | ## 5. Consequences of Unacceptable Behavior
47 |
48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
49 |
50 | Anyone asked to stop unacceptable behavior is expected to comply immediately.
51 |
52 | 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).
53 |
54 | ## 6. Reporting Guidelines
55 |
56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible.
57 |
58 | [Reporting Guidelines](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#contribution-guidelines)
59 |
60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
61 |
62 | ## 7. Addressing Grievances
63 |
64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Avelino with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
65 |
66 | [Policy](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md)
67 |
68 | ## 8. Scope
69 |
70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
71 |
72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
73 |
74 | ## 9. Contact info
75 |
76 | avelinorun AT gmail DOT com
77 |
78 | ## 10. License and attribution
79 |
80 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
81 |
82 | 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).
83 |
84 | Retrieved on November 22, 2016
85 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | This resource was made by the Go community and wouldn't be possible without you!
2 | We appreciate and recognize [all contributors](https://github.com/avelino/awesome-go/graphs/contributors).
3 |
4 |
5 | # Contribution Guidelines
6 |
7 | > 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**.
8 |
9 | - **To add, remove, or change things on the list:** Submit a pull request
10 |
11 | To set this list apart from and complement the excellent [Go wiki Projects page](https://golang.org/wiki/Projects),
12 | and other lists, awesome-go is a specially curated list of high-quality, actively maintained Go packages and resources.
13 |
14 | Please contribute links to packages/projects you have used or are familiar with. This will help ensure high-quality entries.
15 |
16 | > 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
17 |
18 |
19 | ## Quality standards
20 |
21 | To be on the list, project repositories should adhere to the following quality standards.
22 | (https://goreportcard.com/report/github.com/ **github_user** / **github_repo**):
23 |
24 | - have at least 5 months of history since the first commit.
25 | - have an **open source license**, [see list of allowed licenses](https://opensource.org/licenses/alphabetical);
26 | - function as documented and expected;
27 | - be generally useful to the wider community of Go programmers;
28 | - be actively maintained with:
29 | - regular, recent commits;
30 | - or, for finished projects, issues and pull requests are responded to generally within 2 weeks;
31 | - be stable or progressing toward stable;
32 | - 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;
33 | - 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);
34 | - 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.
35 |
36 | Categories must have at least 3 items.
37 |
38 |
39 | ## Preparing for review
40 |
41 | Projects listed must have the following in their documentation. When submitting, you will be asked
42 | to provide them.
43 |
44 | - A link to the project's pkg.go.dev page
45 | - A link to the project's Go Report Card report
46 | - A link to a code coverage report
47 |
48 | One way to accomplish the above is to add badges to your project's README file.
49 | - Use https://pkg.go.dev/badge/ to create the pkg.go.dev link.
50 | - 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.
51 | - 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.
52 |
53 |
54 | ## How to add an item to the list
55 |
56 | Open a pull request against the README.md document that adds the repository to the list.
57 |
58 | - The pull request should add one and only one item to the list.
59 | - The added item should be in alphabetical order within its category.
60 | - The link should be the name of the package or project.
61 | - Descriptions should be clear, concise, and non-promotional.
62 | - Descriptions should follow the link on the same line and end with a punctuation mark.
63 | - Remember to put a period `.` at the end of the project description.
64 |
65 | If you are creating a new category, move the projects that apply to the new category, ensuring
66 | that the resulting list has at least 3 projects in every category, and that the categories are alphabetized.
67 |
68 | 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).
69 |
70 |
71 | ## Congrats, your project got accepted - what now?
72 | You are an outstanding project now! Feel encouraged to tell others about it by adding one of these badges:
73 | [](https://github.com/avelino/awesome-go)
74 | [](https://github.com/avelino/awesome-go)
75 |
76 | ```md
77 | [](https://github.com/avelino/awesome-go)
78 | [](https://github.com/avelino/awesome-go)
79 | ```
80 |
81 |
82 | ## Maintenance expectations for projects listed here
83 |
84 | To prevent removal from awesome-go, your project must maintain the following quality standards.
85 | - Development should be ongoing and maintain code quality. Official releases should be at least once a year if the project is ongoing.
86 | - 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.
87 | - All links to quality reports should be to the most recent official release or current ongoing development.
88 |
89 | Highly recommended but not required:
90 | - A continuous integration process to be part of the ongoing development process
91 | - That the project uses a pull-request process, and the owners do not commit directly to the repository
92 | - That the pull-request process requires the continuous-integration tests to pass before a pull request can be merged
93 |
94 |
95 | ## How to remove an item from the list
96 |
97 | - Open a pull request that deletes the line of the project in question.
98 | - Delete the submission template and substitute a description of which criteria the project is not meeting. It should be a combination of the following.
99 | - The project has not made an official release within the last year and has open issues.
100 | - The project is not responding to bug reports issued within 6 months of submission.
101 | - The project is not meeting quality standards as indicated by the Go Report Card or Code Coverage tests.
102 | - The quality standard links have been removed from the documentation.
103 | - The project is no longer open-sourced.
104 | - The project is incompatible with any Go version issued within the last year (there is hopefully an open PR about this at the project).
105 |
106 | If the project is hosted on GitHub, include a link to the project's submitter and/or author so
107 | that they will be notified of the desire to remove the project and have an opportunity to respond.
108 | The link should be of the form @githubID.
109 |
110 | If the project is not hosted on GitHub, open an issue at the project in question's repository linking to the PR
111 | and stating the following:
112 |
113 | >This project is currently listed at awesome-go at https://github.com/avelino/awesome-go.
114 | However, it appears that the project is not maintaining the quality standards required to continue to be listed at the awesome-go project.
115 | This project is scheduled to be removed within 2 weeks of this posting. To continue to be listed at awesome-go, please respond at:
116 | -- link to above PR --
117 |
118 | Then, comment on your PR at awesome-go with a link to the removal issue at the project.
119 |
120 |
121 | ## Maintainers
122 |
123 | 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.
124 |
125 | The maintainers will review your PR and notify you and tag it in case any
126 | information is still missing. They will wait 15 days for your interaction, after
127 | that the PR will be closed.
128 |
129 |
130 | ## Reporting issues
131 |
132 | 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.
133 |
134 | 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.
135 |
136 | Thanks, everyone!
137 |
138 |
139 | ## How decisions are made
140 |
141 | 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.
142 |
143 |
144 | ## How to become a contributor?
145 |
146 | 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**.
147 |
148 | > Before you do anything, please read [this topic](https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md#quality-standards) very carefully.
149 |
150 | Now that you've read it, let's go!
151 |
152 | Go into the pull requests (PR) and look at the following aspects:
153 |
154 | * **shared links in the body of the PR:** they need to be valid and follow the quality specified above
155 | * **check that the link added to `README.md`** is the same as the link to the repository mentioned in the body of the PR.
156 | * **is it in the correct category?**
157 |
158 | 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.
159 |
160 | **Welcome to awesome-go!**
161 |
162 |
163 | ## How to become an ~~"official maintainer"~~?
164 |
165 | We don't give this name to people who are allowed to accept the PR.
166 |
167 | If you are a person who is constantly active in reviewing PR and contributing to the project, you will be invited by a maintainer.
168 |
169 | > **remember:** if you stop contributing with awesome-go for a long time, you will automatically be removed from the list of maintainers.
170 |
--------------------------------------------------------------------------------
/COVERAGE.md:
--------------------------------------------------------------------------------
1 | # Code Coverage
2 |
3 | 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.
4 |
5 | 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.
6 |
7 | This file should be put in the `.github/workflows` directory of your repo:
8 |
9 | ```yaml
10 | name: Go # The name of the workflow that will appear on Github
11 |
12 | on:
13 | push:
14 | branches: [ main ]
15 | pull_request:
16 | branches: [ main ]
17 | # Allows you to run this workflow manually from the Actions tab
18 | workflow_dispatch:
19 |
20 | jobs:
21 |
22 | build:
23 | runs-on: ${{ matrix.os }}
24 | strategy:
25 | matrix:
26 | os: [ubuntu-latest, windows-latest]
27 | go: [1.16, 1.17]
28 | steps:
29 | - uses: actions/checkout@v2
30 |
31 | - name: Set up Go
32 | uses: actions/setup-go@v2
33 | with:
34 | go-version: ${{ matrix.go }}
35 |
36 | - name: Build
37 | run: go install
38 |
39 | - name: Test
40 | run: |
41 | go test -v -cover ./... -coverprofile coverage.out -coverpkg ./...
42 | go tool cover -func coverage.out -o coverage.out # Replaces coverage.out with the analysis of coverage.out
43 |
44 | - name: Go Coverage Badge
45 | uses: tj-actions/coverage-badge-go@v1
46 | if: ${{ runner.os == 'Linux' && matrix.go == '1.17' }} # Runs this on only one of the ci builds.
47 | with:
48 | green: 80
49 | filename: coverage.out
50 |
51 | - uses: stefanzweifel/git-auto-commit-action@v4
52 | id: auto-commit-action
53 | with:
54 | commit_message: Apply Code Coverage Badge
55 | skip_fetch: true
56 | skip_checkout: true
57 | file_pattern: ./README.md
58 |
59 | - name: Push Changes
60 | if: steps.auto-commit-action.outputs.changes_detected == 'true'
61 | uses: ad-m/github-push-action@master
62 | with:
63 | github_token: ${{ github.token }}
64 | branch: ${{ github.ref }}
65 |
66 | ```
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Thiago Avelino
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MAINTAINERS:
--------------------------------------------------------------------------------
1 | Avelino <avelinorun@gmail.com> (@avelino)
2 | Duke <emersonalmeidax@gmail.com> (@dukex)
3 | Dmitri Shuralyov <dmitri@shuralyov.com> (@dmitshur)
4 | Dobrosław Żybort <matrixik@gmail.com> (@matrixik)
5 | Dean Karn <Dean.Karn@gmail.com> (@joeybloggs)
6 | Kirill Danshin <kirill@danshin.pro> (@kirillDanshin)
7 | Felipe Oliveira <felipeweb.programador@gmail.com> (@felipeweb)
8 | Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
9 | Cássio Botaro <cassiobotaro@gmail.com> (@cassiobotaro)
10 | Jessica Temporal <jessicatemporal@gmail.com> (@jtemporal)
11 | Ceriath <ceriath@ceriath.net> (@ceriath)
12 | Andy Pan <i@andypan.me> (@panjf2000)
13 | Phani Rithvij <phanirithvij2000@gmail.com> (@phanirithvij)
14 | Yassine Benaid <yassinebenaide3@gmail.com> (@yassinebenaid)
15 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/avelino/awesome-go
2 |
3 | go 1.21
4 | toolchain go1.24.1
5 |
6 | require (
7 | github.com/PuerkitoBio/goquery v1.8.1
8 | github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774
9 | github.com/otiai10/copy v1.14.0
10 | github.com/yuin/goldmark v1.6.0
11 | golang.org/x/oauth2 v0.15.0
12 | )
13 |
14 | require (
15 | github.com/andybalholm/cascadia v1.3.1 // indirect
16 | github.com/golang/protobuf v1.5.3 // indirect
17 | golang.org/x/net v0.38.0 // indirect
18 | golang.org/x/sync v0.12.0 // indirect
19 | golang.org/x/sys v0.31.0 // indirect
20 | golang.org/x/text v0.23.0 // indirect
21 | google.golang.org/appengine v1.6.7 // indirect
22 | google.golang.org/protobuf v1.33.0 // indirect
23 | )
24 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
2 | github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
3 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
4 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
5 | github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 h1:HrMVYtly2IVqg9EBooHsakQ256ueojP7QuG32K71X/U=
6 | github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774/go.mod h1:5wi5YYOpfuAKwL5XLFYopbgIl/v7NZxaJpa/4X6yFKE=
7 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
8 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
9 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
10 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
11 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
12 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
13 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
14 | github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
15 | github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
16 | github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
17 | github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
18 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
19 | github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
20 | github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
21 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
22 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
23 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
24 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
25 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
26 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
27 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
28 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
29 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
30 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
31 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
32 | golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
33 | golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
34 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
35 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
36 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
37 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
38 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
39 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
40 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
41 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
43 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
45 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
46 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
47 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
48 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
49 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
50 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
51 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
52 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
53 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
54 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
55 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
56 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
57 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
58 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
59 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
60 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
61 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
62 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
63 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
64 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
65 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
66 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
67 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
68 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
69 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Package main contains code for generate static site.
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "embed"
7 | "errors"
8 | "fmt"
9 | template2 "html/template"
10 | "net/url"
11 | "os"
12 | "path/filepath"
13 | "text/template"
14 |
15 | "github.com/avelino/awesome-go/pkg/markdown"
16 | cp "github.com/otiai10/copy"
17 |
18 | "github.com/PuerkitoBio/goquery"
19 | "github.com/avelino/awesome-go/pkg/slug"
20 | )
21 |
22 | // Link contains info about awesome url
23 | type Link struct {
24 | Title string
25 | URL string
26 | Description string
27 | }
28 |
29 | // Category describe link category
30 | type Category struct {
31 | Title string
32 | Slug string
33 | Description string
34 | Links []Link
35 | }
36 |
37 | // Source files
38 | const readmePath = "README.md"
39 |
40 | // This files should be copied 'as is' to outDir directory
41 | var staticFiles = []string{
42 | "tmpl/assets",
43 | "tmpl/robots.txt",
44 | }
45 |
46 | // Templates
47 | //go:embed tmpl/*.tmpl.html tmpl/*.tmpl.xml
48 | var tplFs embed.FS
49 |
50 | var tpl = template.Must(template.ParseFS(tplFs, "tmpl/*.tmpl.html", "tmpl/*.tmpl.xml"))
51 |
52 | // Output files
53 | const outDir = "out/" // NOTE: trailing slash is required
54 |
55 | var outIndexFile = filepath.Join(outDir, "index.html")
56 | var outSitemapFile = filepath.Join(outDir, "sitemap.xml")
57 |
58 | func main() {
59 | if err := buildStaticSite(); err != nil {
60 | panic(err)
61 | }
62 | }
63 |
64 | func buildStaticSite() error {
65 | if err := dropCreateDir(outDir); err != nil {
66 | return fmt.Errorf("drop-create out dir: %w", err)
67 | }
68 |
69 | if err := renderIndex(readmePath, outIndexFile); err != nil {
70 | return fmt.Errorf("convert markdown to html: %w", err)
71 | }
72 |
73 | input, err := os.ReadFile(outIndexFile)
74 | if err != nil {
75 | return fmt.Errorf("read converted html: %w", err)
76 | }
77 |
78 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(input))
79 | if err != nil {
80 | return fmt.Errorf("create goquery instance: %w", err)
81 | }
82 |
83 | categories, err := extractCategories(doc)
84 | if err != nil {
85 | return fmt.Errorf("extract categories: %w", err)
86 | }
87 |
88 | if err := renderCategories(categories); err != nil {
89 | return fmt.Errorf("render categories: %w", err)
90 | }
91 |
92 | if err := rewriteLinksInIndex(doc, categories); err != nil {
93 | return fmt.Errorf("rewrite links in index: %w", err)
94 | }
95 |
96 | if err := renderSitemap(categories); err != nil {
97 | return fmt.Errorf("render sitemap: %w", err)
98 | }
99 |
100 | for _, srcFilename := range staticFiles {
101 | dstFilename := filepath.Join(outDir, filepath.Base(srcFilename))
102 | fmt.Printf("Copy static file: %s -> %s\n", srcFilename, dstFilename)
103 | if err := cp.Copy(srcFilename, dstFilename); err != nil {
104 | return fmt.Errorf("copy static file `%s` to `%s`: %w", srcFilename, dstFilename, err)
105 | }
106 | }
107 |
108 | return nil
109 | }
110 |
111 | // dropCreateDir drop and create output directory
112 | func dropCreateDir(dir string) error {
113 | if err := os.RemoveAll(dir); err != nil {
114 | return fmt.Errorf("remove dir: %w", err)
115 | }
116 |
117 | if err := mkdirAll(dir); err != nil {
118 | return fmt.Errorf("create dir: %w", err)
119 | }
120 |
121 | return nil
122 | }
123 |
124 | func mkdirAll(path string) error {
125 | _, err := os.Stat(path)
126 | // directory is exists
127 | if err == nil {
128 | return nil
129 | }
130 |
131 | // unexpected error
132 | if !os.IsNotExist(err) {
133 | return fmt.Errorf("unexpected result of dir stat: %w", err)
134 | }
135 |
136 | // directory is not exists
137 | if err := os.MkdirAll(path, 0755); err != nil {
138 | return fmt.Errorf("midirAll: %w", err)
139 | }
140 |
141 | return nil
142 | }
143 |
144 | func renderCategories(categories map[string]Category) error {
145 | for _, category := range categories {
146 | categoryDir := filepath.Join(outDir, category.Slug)
147 | if err := mkdirAll(categoryDir); err != nil {
148 | return fmt.Errorf("create category dir `%s`: %w", categoryDir, err)
149 | }
150 |
151 | // FIXME: embed templates
152 | categoryIndexFilename := filepath.Join(categoryDir, "index.html")
153 | fmt.Printf("Write category Index file: %s\n", categoryIndexFilename)
154 |
155 | buf := bytes.NewBuffer(nil)
156 | if err := tpl.Lookup("category-index.tmpl.html").Execute(buf, category); err != nil {
157 | return fmt.Errorf("render category `%s`: %w", categoryDir, err)
158 | }
159 |
160 | // Sanitize HTML. This is not necessary, but allows to have content
161 | // of all html files in same style.
162 | {
163 | doc, err := goquery.NewDocumentFromReader(buf)
164 | if err != nil {
165 | return fmt.Errorf("create goquery instance for `%s`: %w", categoryDir, err)
166 | }
167 |
168 | html, err := doc.Html()
169 | if err != nil {
170 | return fmt.Errorf("render goquery html for `%s`: %w", categoryDir, err)
171 | }
172 |
173 | if err := os.WriteFile(categoryIndexFilename, []byte(html), 0644); err != nil {
174 | return fmt.Errorf("write category file `%s`: %w", categoryDir, err)
175 | }
176 | }
177 | }
178 |
179 | return nil
180 | }
181 |
182 | func renderSitemap(categories map[string]Category) error {
183 | f, err := os.Create(outSitemapFile)
184 | if err != nil {
185 | return fmt.Errorf("create sitemap file `%s`: %w", outSitemapFile, err)
186 | }
187 |
188 | fmt.Printf("Render Sitemap to: %s\n", outSitemapFile)
189 |
190 | if err := tpl.Lookup("sitemap.tmpl.xml").Execute(f, categories); err != nil {
191 | return fmt.Errorf("render sitemap: %w", err)
192 | }
193 |
194 | return nil
195 | }
196 |
197 | func extractCategories(doc *goquery.Document) (map[string]Category, error) {
198 | categories := make(map[string]Category)
199 | var rootErr error
200 |
201 | doc.
202 | Find("body #contents").
203 | NextFiltered("ul").
204 | Find("ul").
205 | EachWithBreak(func(_ int, selUl *goquery.Selection) bool {
206 | if rootErr != nil {
207 | return false
208 | }
209 |
210 | selUl.
211 | Find("li a").
212 | EachWithBreak(func(_ int, s *goquery.Selection) bool {
213 | selector, exists := s.Attr("href")
214 | if !exists {
215 | return true
216 | }
217 |
218 | category, err := extractCategory(doc, selector)
219 | if err != nil {
220 | rootErr = fmt.Errorf("extract category: %w", err)
221 | return false
222 | }
223 |
224 | categories[selector] = *category
225 |
226 | return true
227 | })
228 |
229 | return true
230 | })
231 |
232 | if rootErr != nil {
233 | return nil, fmt.Errorf("extract categories: %w", rootErr)
234 | }
235 |
236 | return categories, nil
237 | }
238 |
239 | func extractCategory(doc *goquery.Document, selector string) (*Category, error) {
240 | var category Category
241 | var err error
242 |
243 | doc.Find(selector).EachWithBreak(func(_ int, selCatHeader *goquery.Selection) bool {
244 | selDescr := selCatHeader.NextFiltered("p")
245 | // FIXME: bug. this would select links from all neighboring
246 | // sub-categories until the next category. To prevent this we should
247 | // find only first ul
248 | ul := selCatHeader.NextFilteredUntil("ul", "h2")
249 |
250 | var links []Link
251 | ul.Find("li").Each(func(_ int, selLi *goquery.Selection) {
252 | selLink := selLi.Find("a")
253 | url, _ := selLink.Attr("href")
254 | link := Link{
255 | Title: selLink.Text(),
256 | // FIXME(kazhuravlev): Title contains only title but
257 | // description contains Title + description
258 | Description: selLi.Text(),
259 | URL: url,
260 | }
261 | links = append(links, link)
262 | })
263 |
264 | // FIXME: In this case we would have an empty category in main index.html with link to 404 page.
265 | if len(links) == 0 {
266 | err = errors.New("category does not contain links")
267 | return false
268 | }
269 |
270 | category = Category{
271 | Slug: slug.Generate(selCatHeader.Text()),
272 | Title: selCatHeader.Text(),
273 | Description: selDescr.Text(),
274 | Links: links,
275 | }
276 |
277 | return true
278 | })
279 |
280 | if err != nil {
281 | return nil, fmt.Errorf("build a category: %w", err)
282 | }
283 |
284 | return &category, nil
285 | }
286 |
287 | func rewriteLinksInIndex(doc *goquery.Document, categories map[string]Category) error {
288 | var iterErr error
289 | doc.
290 | Find("body #content ul li ul li a").
291 | EachWithBreak(func(_ int, s *goquery.Selection) bool {
292 | href, hrefExists := s.Attr("href")
293 | if !hrefExists {
294 | // FIXME: looks like is an error. Tag `a` in our case always
295 | // should have `href` attr.
296 | return true
297 | }
298 |
299 | // do not replace links if no page has been created for it
300 | _, catExists := categories[href]
301 | if !catExists {
302 | return true
303 | }
304 |
305 | linkURL, err := url.Parse(href)
306 | if err != nil {
307 | iterErr = err
308 | return false
309 | }
310 |
311 | if linkURL.Fragment != "" && linkURL.Fragment != "contents" {
312 | s.SetAttr("href", linkURL.Fragment)
313 | }
314 |
315 | return true
316 | })
317 |
318 | if iterErr != nil {
319 | return iterErr
320 | }
321 |
322 | fmt.Printf("Rewrite links in Index file: %s\n", outIndexFile)
323 | resultHTML, err := doc.Html()
324 | if err != nil {
325 | return fmt.Errorf("render html: %w", err)
326 | }
327 |
328 | if err := os.WriteFile(outIndexFile, []byte(resultHTML), 0644); err != nil {
329 | return fmt.Errorf("rewrite index file: %w", err)
330 | }
331 |
332 | return nil
333 | }
334 |
335 | // renderIndex generate site html (index.html) from markdown file
336 | func renderIndex(srcFilename, outFilename string) error {
337 | input, err := os.ReadFile(srcFilename)
338 | if err != nil {
339 | return err
340 | }
341 |
342 | body, err := markdown.ToHTML(input)
343 | if err != nil {
344 | return err
345 | }
346 |
347 | f, err := os.Create(outFilename)
348 | if err != nil {
349 | return err
350 | }
351 |
352 | fmt.Printf("Write Index file: %s\n", outIndexFile)
353 | data := map[string]interface{}{
354 | "Body": template2.HTML(body),
355 | }
356 | if err := tpl.Lookup("index.tmpl.html").Execute(f, data); err != nil {
357 | return err
358 | }
359 |
360 | if err := f.Close(); err != nil {
361 | return fmt.Errorf("close index file: %w", err)
362 | }
363 |
364 | return nil
365 | }
366 |
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "github.com/avelino/awesome-go/pkg/markdown"
6 | "os"
7 | "regexp"
8 | "sort"
9 | "strings"
10 | "testing"
11 |
12 | "github.com/PuerkitoBio/goquery"
13 | )
14 |
15 | var (
16 | reContainsLink = regexp.MustCompile(`\* \[.*\]\(.*\)`)
17 | reOnlyLink = regexp.MustCompile(`\* \[.*\]\([^()]*\)
max tokens
The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by
removing the max tokens filter.