├── .circleci └── config.yml ├── .dockerignore ├── .github ├── CODE_OF_CONDUCT.md ├── dependabot.yml └── workflows │ └── docker.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── compose-admin.yaml ├── compose.yaml ├── db.go ├── db_test.go ├── docker ├── initdb.d │ ├── 01-schema.sql │ └── 02-data.sql └── nginx │ └── default.conf ├── go.mod ├── go.sum ├── handlers.go ├── handlers_test.go ├── main.go ├── mozlog ├── mozlog.go └── mozlog_test.go ├── params.go ├── scripts └── create_docker_testdb ├── vendor ├── filippo.io │ └── edwards25519 │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── edwards25519.go │ │ ├── extra.go │ │ ├── field │ │ ├── fe.go │ │ ├── fe_amd64.go │ │ ├── fe_amd64.s │ │ ├── fe_amd64_noasm.go │ │ ├── fe_arm64.go │ │ ├── fe_arm64.s │ │ ├── fe_arm64_noasm.go │ │ ├── fe_extra.go │ │ └── fe_generic.go │ │ ├── scalar.go │ │ ├── scalar_fiat.go │ │ ├── scalarmult.go │ │ └── tables.go ├── github.com │ ├── cpuguy83 │ │ └── go-md2man │ │ │ └── v2 │ │ │ ├── LICENSE.md │ │ │ └── md2man │ │ │ ├── debug.go │ │ │ ├── md2man.go │ │ │ └── roff.go │ ├── davecgh │ │ └── go-spew │ │ │ ├── LICENSE │ │ │ └── spew │ │ │ ├── bypass.go │ │ │ ├── bypasssafe.go │ │ │ ├── common.go │ │ │ ├── config.go │ │ │ ├── doc.go │ │ │ ├── dump.go │ │ │ ├── format.go │ │ │ └── spew.go │ ├── go-sql-driver │ │ └── mysql │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── auth.go │ │ │ ├── buffer.go │ │ │ ├── collations.go │ │ │ ├── compress.go │ │ │ ├── conncheck.go │ │ │ ├── conncheck_dummy.go │ │ │ ├── connection.go │ │ │ ├── connector.go │ │ │ ├── const.go │ │ │ ├── driver.go │ │ │ ├── dsn.go │ │ │ ├── errors.go │ │ │ ├── fields.go │ │ │ ├── infile.go │ │ │ ├── nulltime.go │ │ │ ├── packets.go │ │ │ ├── result.go │ │ │ ├── rows.go │ │ │ ├── statement.go │ │ │ ├── transaction.go │ │ │ └── utils.go │ ├── pmezard │ │ └── go-difflib │ │ │ ├── LICENSE │ │ │ └── difflib │ │ │ └── difflib.go │ ├── russross │ │ └── blackfriday │ │ │ └── v2 │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE.txt │ │ │ ├── README.md │ │ │ ├── block.go │ │ │ ├── doc.go │ │ │ ├── entities.go │ │ │ ├── esc.go │ │ │ ├── html.go │ │ │ ├── inline.go │ │ │ ├── markdown.go │ │ │ ├── node.go │ │ │ └── smartypants.go │ ├── stretchr │ │ └── testify │ │ │ ├── LICENSE │ │ │ └── assert │ │ │ ├── assertion_compare.go │ │ │ ├── assertion_format.go │ │ │ ├── assertion_format.go.tmpl │ │ │ ├── assertion_forward.go │ │ │ ├── assertion_forward.go.tmpl │ │ │ ├── assertion_order.go │ │ │ ├── assertions.go │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── forward_assertions.go │ │ │ ├── http_assertions.go │ │ │ └── yaml │ │ │ ├── yaml_custom.go │ │ │ ├── yaml_default.go │ │ │ └── yaml_fail.go │ └── urfave │ │ └── cli │ │ ├── .flake8 │ │ ├── .gitignore │ │ ├── CODE_OF_CONDUCT.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── app.go │ │ ├── category.go │ │ ├── cli.go │ │ ├── command.go │ │ ├── context.go │ │ ├── docs.go │ │ ├── errors.go │ │ ├── fish.go │ │ ├── flag.go │ │ ├── flag_bool.go │ │ ├── flag_bool_t.go │ │ ├── flag_duration.go │ │ ├── flag_float64.go │ │ ├── flag_generic.go │ │ ├── flag_int.go │ │ ├── flag_int64.go │ │ ├── flag_int64_slice.go │ │ ├── flag_int_slice.go │ │ ├── flag_string.go │ │ ├── flag_string_slice.go │ │ ├── flag_uint.go │ │ ├── flag_uint64.go │ │ ├── funcs.go │ │ ├── help.go │ │ ├── parse.go │ │ ├── sort.go │ │ └── template.go ├── gopkg.in │ └── yaml.v3 │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ ├── apic.go │ │ ├── decode.go │ │ ├── emitterc.go │ │ ├── encode.go │ │ ├── parserc.go │ │ ├── readerc.go │ │ ├── resolve.go │ │ ├── scannerc.go │ │ ├── sorter.go │ │ ├── writerc.go │ │ ├── yaml.go │ │ ├── yamlh.go │ │ └── yamlprivateh.go └── modules.txt └── version.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # These environment variables must be set in CircleCI UI 2 | # 3 | # DOCKERHUB_REPO - docker hub repo, format: / 4 | # DOCKER_USER - login info for docker hub 5 | # DOCKER_PASS 6 | # 7 | version: 2.1 8 | 9 | orbs: 10 | codecov: codecov/codecov@3.2.4 11 | 12 | jobs: 13 | build: 14 | docker: 15 | - image: docker:stable-git 16 | working_directory: /dockerflow 17 | steps: 18 | - checkout 19 | - setup_remote_docker 20 | 21 | - run: 22 | name: Create a version.json 23 | command: | 24 | # create a version.json per https://github.com/mozilla-services/Dockerflow/blob/master/docs/version_object.md 25 | printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' \ 26 | "$CIRCLE_SHA1" \ 27 | "$CIRCLE_TAG" \ 28 | "$CIRCLE_PROJECT_USERNAME" \ 29 | "$CIRCLE_PROJECT_REPONAME" \ 30 | "$CIRCLE_BUILD_URL" > version.json 31 | - run: 32 | name: Build Docker image 33 | command: docker build -t app:build . 34 | 35 | # save the built docker container into CircleCI's cache. This is 36 | # required since Workflows do not have the same remote docker instance. 37 | - run: 38 | name: docker save app:build 39 | command: docker save -o /tmp/docker.tar "app:build" 40 | - save_cache: 41 | key: v1-{{ .Branch }}-{{epoch}} 42 | paths: 43 | - /tmp/docker.tar 44 | 45 | test: 46 | docker: 47 | - image: cimg/go:1.23 48 | - image: cimg/mysql:5.7 49 | steps: 50 | - checkout 51 | - run: 52 | name: Waiting for MySQL to be ready 53 | command: | 54 | for i in `seq 1 10`; 55 | do 56 | nc -z 127.0.0.1 3306 && echo Success && exit 0 57 | echo -n . 58 | sleep 1 59 | done 60 | echo Failed waiting for MySQL && exit 1 61 | - run: 62 | name: Install MySQL CLI; Import test data 63 | command: | 64 | sudo apt update && sudo apt install -y default-mysql-client 65 | mysql -u root -h 127.0.0.1 --port=3306 -e 'create database bouncer_test;' 66 | mysql -u root -h 127.0.0.1 --port=3306 bouncer_test < ./docker/initdb.d/01-schema.sql 67 | mysql -u root -h 127.0.0.1 --port=3306 bouncer_test < ./docker/initdb.d/02-data.sql 68 | - run: 69 | name: Get dependencies 70 | command: | 71 | go get 72 | - run: 73 | name: Go test 74 | command: | 75 | go test -v -mod vendor -covermode=atomic -coverprofile=coverage.txt ./... 76 | - run: 77 | name: Go lint 78 | command: | 79 | go vet ./... 80 | 81 | go install honnef.co/go/tools/cmd/staticcheck@latest 82 | $(go env GOPATH)/bin/staticcheck ./... 83 | - codecov/upload 84 | 85 | deploy: 86 | docker: 87 | - image: cimg/base:2023.04 88 | steps: 89 | - setup_remote_docker 90 | - restore_cache: 91 | key: v1-{{.Branch}} 92 | - run: 93 | name: Restore Docker image cache 94 | command: docker load -i /tmp/docker.tar 95 | 96 | - run: 97 | name: Deploy to Dockerhub 98 | command: | 99 | echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin 100 | # deploy master 101 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 102 | docker tag app:build ${DOCKERHUB_REPO}:latest 103 | docker push ${DOCKERHUB_REPO}:latest 104 | elif [ ! -z "${CIRCLE_TAG}" ]; then 105 | # deploy a release tag... 106 | echo "${DOCKERHUB_REPO}:${CIRCLE_TAG}" 107 | docker tag app:build "${DOCKERHUB_REPO}:${CIRCLE_TAG}" 108 | docker images 109 | docker push "${DOCKERHUB_REPO}:${CIRCLE_TAG}" 110 | fi 111 | 112 | workflows: 113 | version: 2 114 | build-test-deploy: 115 | jobs: 116 | - build: 117 | filters: 118 | tags: 119 | only: /.*/ 120 | 121 | - test: 122 | filters: 123 | tags: 124 | only: /.*/ 125 | 126 | - deploy: 127 | requires: 128 | - build 129 | filters: 130 | tags: 131 | only: /.*/ 132 | branches: 133 | only: master 134 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .circleci/ 2 | .github/ 3 | docker/ 4 | scripts/ 5 | 6 | **/*_test.go 7 | README.md 8 | compose.yaml 9 | compose-admin.yaml 10 | 11 | # Ignore generated credentials from google-github-actions/auth 12 | gha-creds-*.json 13 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker build & push 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | tags: 9 | - '*' 10 | jobs: 11 | docker: 12 | name: Docker Images 13 | runs-on: ubuntu-latest 14 | environment: build 15 | permissions: 16 | contents: read 17 | id-token: write 18 | steps: 19 | - name: Clone repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup Docker Buildx 23 | uses: docker/setup-buildx-action@v3 24 | 25 | - name: Docker Metadata 26 | id: meta 27 | uses: docker/metadata-action@v5 28 | with: 29 | images: | 30 | ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ vars.GCP_PROJECT_ID }}/${{ vars.GAR_REPOSITORY}}/${{ vars.GAR_NAME }} 31 | tags: | 32 | # set latest tag for default branch 33 | type=raw,value=latest,enable={{is_default_branch}} 34 | # tag event 35 | type=ref,event=tag 36 | 37 | - name: Generate version.json 38 | shell: bash 39 | run: | 40 | printf '{"commit":"%s","version":"%s","source":"https://github.com/%s","build":"%s/%s/actions/runs/%s"}\n' \ 41 | "$GITHUB_SHA" \ 42 | "$GITHUB_REF_NAME" \ 43 | "$GITHUB_REPOSITORY" \ 44 | "$GITHUB_SERVER_URL" \ 45 | "$GITHUB_REPOSITORY" \ 46 | "$GITHUB_RUN_ID" | tee version.json 47 | 48 | - id: gcp-auth 49 | uses: google-github-actions/auth@v2 50 | with: 51 | token_format: 'access_token' 52 | service_account: artifact-writer@${{ vars.GCP_PROJECT_ID}}.iam.gserviceaccount.com 53 | workload_identity_provider: ${{ vars.GCPV2_GITHUB_WORKLOAD_IDENTITY_PROVIDER }} 54 | 55 | - name: Login to Google Artifact Registry 56 | uses: docker/login-action@v3 57 | with: 58 | registry: ${{ vars.GAR_LOCATION }}-docker.pkg.dev 59 | username: oauth2accesstoken 60 | password: ${{ steps.gcp-auth.outputs.access_token }} 61 | 62 | - name: Build and push 63 | uses: docker/build-push-action@v6 64 | with: 65 | push: ${{ github.event_name != 'pull_request' }} 66 | sbom: true 67 | tags: ${{ steps.meta.outputs.tags }} 68 | labels: ${{ steps.meta.outputs.labels }} 69 | context: . 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This should be a directory, or a symlink so that why the trailing slash is 2 | # omitted. 3 | /bouncer-admin 4 | /coverage.txt 5 | /go-bouncer 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23 2 | 3 | ENV PROJECT=github.com/mozilla-services/go-bouncer 4 | 5 | COPY . /app 6 | 7 | EXPOSE 8000 8 | 9 | WORKDIR /app 10 | 11 | RUN go install -mod vendor $PROJECT 12 | 13 | CMD ["go-bouncer", "--addr", "127.0.0.1:8000"] 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-bouncer 2 | 3 | [![CircleCI](https://circleci.com/gh/mozilla-services/go-bouncer/tree/master.svg?style=svg)](https://circleci.com/gh/mozilla-services/go-bouncer/?branch=master) 4 | 5 | The project behind https://download.mozilla.org/ :fire: 6 | 7 | ## Getting started (for development) 8 | 9 | ``` 10 | docker compose up -d 11 | ``` 12 | 13 | You can then call the service using `127.0.0.1:8000` directly or via the Nginx 14 | proxy at `127.0.0.1:18000`: 15 | 16 | ``` 17 | $ curl -H "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)" -I 'http://127.0.0.1:8000/?product=firefox-ssl&os=win' 18 | HTTP/1.1 302 Found 19 | Cache-Control: max-age=60 20 | Content-Type: text/html; charset=utf-8 21 | Location: 22 | https://download-installer.cdn.mozilla.net/pub/firefox/releases/43.0.1/win32/en-US/Firefox%20Setup%2043.0.1.exe 23 | Date: Wed, 23 Oct 2024 08:24:42 GMT 24 | 25 | $ curl -H "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)" -I 'http://127.0.0.1:18000/?product=firefox-ssl&os=win' 26 | HTTP/1.1 302 Found 27 | Server: nginx/1.25.5 28 | Date: Wed, 23 Oct 2024 08:24:43 GMT 29 | Content-Type: text/html; charset=utf-8 30 | Content-Length: 134 31 | Connection: keep-alive 32 | Cache-Control: max-age=60 33 | Location: https://download-installer.cdn.mozilla.net/pub/firefox/releases/43.0.1/win32/en-US/Firefox%20Setup%2043.0.1.exe 34 | x-debug-cache-key: upstream_bouncer/?product=firefox-ssl&os=winwinxpother 35 | ``` 36 | 37 | This nginx config looks similar to the one we have on production but it isn't 38 | exactly the same. In addition to that, it adds some debugging capabilities like 39 | the following headers: 40 | 41 | - `x-debug-cache-key`: the computed cache key 42 | - `x-debug-referer`: the referer value, if any 43 | 44 | ### Setting up `bouncer-admin` in localdev 45 | 46 | [bouncer-admin][] is the admin interface for go-bouncer. It can be optionally 47 | set up by first cloning the repository (once): 48 | 49 | ``` 50 | git clone https://github.com/mozilla-services/bouncer-admin 51 | ``` 52 | 53 | Then, run `docker compose` as follows: 54 | 55 | ``` 56 | docker compose -f compose.yaml -f compose-admin.yaml up -d 57 | ``` 58 | 59 | Note: Every `docker compose` needs to specify both configuration files if the 60 | intent is to interact with the `admin` container, e.g. `docker compose -f 61 | compose.yaml -f compose-admin.yaml logs -f admin`. 62 | 63 | The API is available at: http://127.0.0.1:9000/api/. The authenticated user is 64 | `admin` with the traditional `admin` password. Here is an example to create a 65 | new product: 66 | 67 | ``` 68 | curl -X POST 'http://admin:admin@127.0.0.1:9000/api/product_add/' -d 'product=A-Test-Product&ssl_only=true' 69 | 70 | 71 | 72 | 73 | ``` 74 | 75 | ## Environment variables 76 | 77 | ### `BOUNCER_ADDR` 78 | 79 | Address on which to listen. The default value is: `:8888` 80 | 81 | ### `BOUNCER_DB_DSN` 82 | 83 | Database DSN, see: https://github.com/go-sql-driver/mysql#dsn-data-source-name 84 | for more details about the format. 85 | 86 | ### `BOUNCER_PIN_HTTPS_HEADER_NAME` 87 | 88 | When this flag is set and the request header value equals https, an HTTPS 89 | redirect will always be returned. The default value is: `X-Forwarded-Proto`, 90 | which means this feature is enabled by default. 91 | 92 | ### `BOUNCER_PINNED_BASEURL_HTTP` 93 | 94 | Configure the base URL for non-SSL only products. 95 | 96 | ### `BOUNCER_PINNED_BASEURL_HTTPS` 97 | 98 | Same as `BOUNCER_PINNED_BASEURL_HTTP` but SSL-only products. 99 | 100 | ### `BOUNCER_STUB_ROOT_URL` 101 | 102 | Optional. If set, bouncer will redirect requests with `attribution_sig` and 103 | `attribution_code` parameters to the stubattribution service using this URL: 104 | 105 | ``` 106 | BOUNCER_STUB_ROOT_URL?product=PRODUCT&os=OS&lang=LANG&attribution_sig=ATTRIBUTION_SIG&attribution_code=ATTRIBUTION_CODE 107 | ``` 108 | 109 | [go-bouncer]: https://github.com/mozilla-services/go-bouncer/ 110 | [bouncer-admin]: https://github.com/mozilla-services/bouncer-admin/ 111 | -------------------------------------------------------------------------------- /compose-admin.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | admin: 3 | build: 4 | context: ./bouncer-admin/ 5 | ports: 6 | - 9000:8000 7 | environment: 8 | DATABASE_URL: mysql 9 | DB_USER: bounceruser 10 | DB_PASS: bouncerpass 11 | DB_NAME: bouncerdb 12 | AUTH_USERS: '{"admin": "admin"}' 13 | # This is (mainly) used to get stacktraces in the container logs. 14 | FLASK_ENV: development 15 | depends_on: 16 | mysql: 17 | condition: service_healthy 18 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | bouncer: 3 | build: 4 | context: . 5 | command: go-bouncer 6 | ports: 7 | - 8000:8000 8 | environment: 9 | BOUNCER_ADDR: "0.0.0.0:8000" 10 | BOUNCER_DB_DSN: "bounceruser:bouncerpass@tcp(mysql:3306)/bouncerdb" 11 | BOUNCER_PINNED_BASEURL_HTTP: download.cdn.mozilla.net/pub 12 | BOUNCER_PINNED_BASEURL_HTTPS: download-installer.cdn.mozilla.net/pub 13 | depends_on: 14 | mysql: 15 | condition: service_healthy 16 | mysql: 17 | image: mysql 18 | environment: 19 | MYSQL_ROOT_PASSWORD: rootpass 20 | MYSQL_DATABASE: bouncerdb 21 | MYSQL_USER: bounceruser 22 | MYSQL_PASSWORD: bouncerpass 23 | volumes: 24 | - ./docker/initdb.d/:/docker-entrypoint-initdb.d/ 25 | healthcheck: 26 | test: ["CMD", "mysql", "-u", "root", "-prootpass", "--execute", "SHOW DATABASES;"] 27 | interval: 3s 28 | retries: 5 29 | timeout: 5s 30 | nginx: 31 | image: nginx 32 | volumes: 33 | - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf 34 | ports: 35 | - 18000:80 36 | depends_on: 37 | - bouncer 38 | -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | 6 | _ "github.com/go-sql-driver/mysql" 7 | ) 8 | 9 | // DB is a DB instance for running queries against the bouncer database 10 | type DB struct { 11 | *sql.DB 12 | } 13 | 14 | // NewDB returns a new database instance. 15 | func NewDB(dsn string) (*DB, error) { 16 | db, err := sql.Open("mysql", dsn) 17 | 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | err = db.Ping() 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return &DB{ 28 | DB: db, 29 | }, nil 30 | } 31 | 32 | // AliasFor returns the alias for a product 33 | // 34 | // For example firefox-latest will resolve to the latest version of firefox. 35 | func (d *DB) AliasFor(product string) (related string, err error) { 36 | err = d.QueryRow( 37 | "SELECT related_product FROM mirror_aliases WHERE alias = ?", 38 | product).Scan(&related) 39 | 40 | if err != nil { 41 | if err == sql.ErrNoRows { 42 | return product, nil 43 | } 44 | return "", err 45 | } 46 | return 47 | } 48 | 49 | // OSID returns the id of an operation system, by name 50 | func (d *DB) OSID(name string) (id string, err error) { 51 | err = d.QueryRow( 52 | "SELECT id FROM mirror_os WHERE name = ?", 53 | name).Scan(&id) 54 | 55 | return 56 | } 57 | 58 | // ProductForLanguage returns the product ID given a product name and language. 59 | func (d *DB) ProductForLanguage(product, lang string) (productID string, sslOnly bool, err error) { 60 | sslInt := 0 61 | err = d.QueryRow( 62 | `SELECT prod.id, prod.ssl_only FROM mirror_products AS prod 63 | LEFT JOIN mirror_product_langs AS langs ON (prod.id = langs.product_id) 64 | WHERE prod.name LIKE ? 65 | AND (langs.language LIKE ? OR langs.language IS NULL)`, 66 | product, lang).Scan(&productID, &sslInt) 67 | 68 | if sslInt == 1 { 69 | sslOnly = true 70 | } else { 71 | sslOnly = false 72 | } 73 | return 74 | } 75 | 76 | // Location returns the path of the product/os combination 77 | func (d *DB) Location(productID, osID string) (id, path string, err error) { 78 | err = d.QueryRow( 79 | `SELECT id, path FROM mirror_locations 80 | WHERE product_id = ? AND os_id = ?`, 81 | productID, osID).Scan(&id, &path) 82 | 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /db_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | var testDB *DB 13 | 14 | func TestMain(m *testing.M) { 15 | var err error 16 | testDB, err = NewDB("root@tcp(127.0.0.1:3306)/bouncer_test") 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | defer testDB.Close() 21 | os.Exit(m.Run()) 22 | } 23 | 24 | func TestAliasFor(t *testing.T) { 25 | res, err := testDB.AliasFor("firefox-latest") 26 | assert.NoError(t, err) 27 | assert.Equal(t, "Firefox", res) 28 | 29 | res, err = testDB.AliasFor("firefox-123") 30 | assert.NoError(t, err) 31 | // When the alias is not found, we return the product. 32 | assert.Equal(t, "firefox-123", res) 33 | } 34 | 35 | func TestOSID(t *testing.T) { 36 | res, err := testDB.OSID("win64") 37 | assert.NoError(t, err) 38 | assert.Equal(t, "1", res) 39 | } 40 | 41 | func TestProductForLanguage(t *testing.T) { 42 | res, sslOnly, err := testDB.ProductForLanguage("Firefox", "en-US") 43 | assert.NoError(t, err) 44 | assert.False(t, sslOnly) 45 | assert.Equal(t, "1", res) 46 | 47 | res, sslOnly, err = testDB.ProductForLanguage("Firefox-SSL", "en-US") 48 | assert.NoError(t, err) 49 | assert.True(t, sslOnly) 50 | assert.Equal(t, "2", res) 51 | } 52 | 53 | func TestLocation(t *testing.T) { 54 | // We need some IDs before we can invoke `Location()`. 55 | productID, _, _ := testDB.ProductForLanguage("Firefox", "en-US") 56 | osID, _ := testDB.OSID("win64") 57 | 58 | id, path, err := testDB.Location(productID, osID) 59 | assert.NoError(t, err) 60 | assert.Equal(t, "1", id) 61 | assert.Equal(t, "/firefox/releases/39.0/win64/:lang/Firefox%20Setup%2039.0.exe", path) 62 | 63 | // Unknown location 64 | _, _, err = testDB.Location("some-product-id", osID) 65 | assert.Error(t, err, sql.ErrNoRows) 66 | } 67 | -------------------------------------------------------------------------------- /docker/initdb.d/01-schema.sql: -------------------------------------------------------------------------------- 1 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 2 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 3 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 4 | /*!40101 SET NAMES utf8 */; 5 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 6 | /*!40103 SET TIME_ZONE='+00:00' */; 7 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 8 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 9 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 10 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 11 | 12 | DROP TABLE IF EXISTS `mirror_aliases`; 13 | /*!40101 SET @saved_cs_client = @@character_set_client */; 14 | /*!40101 SET character_set_client = utf8 */; 15 | CREATE TABLE `mirror_aliases` ( 16 | `id` int(11) NOT NULL AUTO_INCREMENT, 17 | `alias` varchar(255) NOT NULL, 18 | `related_product` varchar(255) NOT NULL, 19 | PRIMARY KEY (`id`), 20 | UNIQUE KEY `alias` (`alias`), 21 | UNIQUE KEY `uniq_alias` (`alias`) 22 | ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 23 | /*!40101 SET character_set_client = @saved_cs_client */; 24 | 25 | DROP TABLE IF EXISTS `mirror_locations`; 26 | /*!40101 SET @saved_cs_client = @@character_set_client */; 27 | /*!40101 SET character_set_client = utf8 */; 28 | CREATE TABLE `mirror_locations` ( 29 | `id` int(11) NOT NULL AUTO_INCREMENT, 30 | `product_id` int(11) NOT NULL DEFAULT '0', 31 | `os_id` int(11) NOT NULL DEFAULT '0', 32 | `path` varchar(255) NOT NULL DEFAULT '', 33 | PRIMARY KEY (`id`,`product_id`,`os_id`), 34 | KEY `product_os_idx` (`product_id`,`os_id`) 35 | ) ENGINE=InnoDB AUTO_INCREMENT=23194 DEFAULT CHARSET=utf8; 36 | /*!40101 SET character_set_client = @saved_cs_client */; 37 | 38 | DROP TABLE IF EXISTS `mirror_os`; 39 | /*!40101 SET @saved_cs_client = @@character_set_client */; 40 | /*!40101 SET character_set_client = utf8 */; 41 | CREATE TABLE `mirror_os` ( 42 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 43 | `name` varchar(32) NOT NULL DEFAULT '', 44 | `priority` int(11) NOT NULL DEFAULT '0', 45 | PRIMARY KEY (`id`,`name`) 46 | ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; 47 | /*!40101 SET character_set_client = @saved_cs_client */; 48 | 49 | DROP TABLE IF EXISTS `mirror_product_langs`; 50 | /*!40101 SET @saved_cs_client = @@character_set_client */; 51 | /*!40101 SET character_set_client = utf8 */; 52 | CREATE TABLE `mirror_product_langs` ( 53 | `id` int(11) NOT NULL AUTO_INCREMENT, 54 | `product_id` int(11) NOT NULL, 55 | `language` varchar(30) NOT NULL, 56 | PRIMARY KEY (`id`), 57 | UNIQUE KEY `product_id` (`product_id`,`language`) 58 | ) ENGINE=InnoDB AUTO_INCREMENT=222137 DEFAULT CHARSET=utf8; 59 | /*!40101 SET character_set_client = @saved_cs_client */; 60 | 61 | DROP TABLE IF EXISTS `mirror_products`; 62 | /*!40101 SET @saved_cs_client = @@character_set_client */; 63 | /*!40101 SET character_set_client = utf8 */; 64 | CREATE TABLE `mirror_products` ( 65 | `id` int(11) NOT NULL AUTO_INCREMENT, 66 | `name` varchar(255) NOT NULL DEFAULT '', 67 | `priority` int(11) NOT NULL DEFAULT '0', 68 | `count` bigint(20) unsigned NOT NULL DEFAULT '0', 69 | `active` tinyint(4) NOT NULL DEFAULT '1', 70 | `checknow` tinyint(1) unsigned NOT NULL DEFAULT '1', 71 | `ssl_only` tinyint(1) NOT NULL DEFAULT '0', 72 | PRIMARY KEY (`id`), 73 | UNIQUE KEY `product_name` (`name`), 74 | KEY `product_count` (`count`), 75 | KEY `product_active` (`active`), 76 | KEY `product_checknow` (`checknow`) 77 | ) ENGINE=InnoDB AUTO_INCREMENT=4556 DEFAULT CHARSET=utf8; 78 | /*!40101 SET character_set_client = @saved_cs_client */; 79 | 80 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 81 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 82 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 83 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 84 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 85 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 86 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 87 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 88 | -------------------------------------------------------------------------------- /docker/nginx/default.conf: -------------------------------------------------------------------------------- 1 | proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=bouncer_zone:100m; 2 | 3 | upstream upstream_bouncer { 4 | server bouncer:8000; 5 | } 6 | 7 | map $http_user_agent $ua_bucket { 8 | default "other"; 9 | 10 | "~*Windows (NT 5\.1|XP|NT 5\.2|NT 6\.0)" "winxp"; 11 | "NSIS InetBgDL (Mozilla)" "pre2024stub"; 12 | "~*Windows NT 6\.(1|2|3).+?(Win64|WOW64)" "win7x64"; 13 | "~*Windows NT 6\.(1|2|3)" "win7"; 14 | } 15 | 16 | map $http_referer $referer_bucket { 17 | default "other"; 18 | 19 | "~^https://www\.mozilla\.org/" "mozorg"; 20 | "~^https://www\.firefox\.com/" "fxc"; 21 | } 22 | 23 | server { 24 | listen 80; 25 | 26 | proxy_cache_key $http_x_forwarded_proto$proxy_host$request_uri$ua_bucket$referer_bucket; 27 | 28 | location / { 29 | proxy_ignore_headers Vary; 30 | proxy_set_header Host $http_host; 31 | proxy_redirect off; 32 | proxy_pass http://upstream_bouncer; 33 | 34 | proxy_cache bouncer_zone; 35 | proxy_cache_valid 200 302 301 5m; 36 | proxy_cache_valid 404 1m; 37 | proxy_cache_lock on; 38 | 39 | add_header x-debug-referer $http_referer; 40 | add_header x-debug-cache-key $http_x_forwarded_proto$proxy_host$request_uri$ua_bucket$referer_bucket; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mozilla-services/go-bouncer 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.9.3 7 | github.com/stretchr/testify v1.11.1 8 | github.com/urfave/cli v1.22.17 9 | ) 10 | 11 | require ( 12 | filippo.io/edwards25519 v1.1.0 // indirect 13 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/pmezard/go-difflib v1.0.0 // indirect 16 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 17 | gopkg.in/yaml.v3 v3.0.1 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= 2 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 3 | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= 10 | github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 14 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 16 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 17 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 18 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 19 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 20 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 21 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 22 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 23 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 24 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 25 | github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= 26 | github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= 27 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 29 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // go-bouncer is the web service behind https://download.mozilla.org/. 2 | // Its purpose is to serve builds given a product, OS, and language. 3 | package main 4 | 5 | import ( 6 | "log" 7 | "net/http" 8 | "os" 9 | "time" 10 | 11 | "github.com/urfave/cli" 12 | 13 | _ "github.com/mozilla-services/go-bouncer/mozlog" 14 | ) 15 | 16 | const ( 17 | // versionFilePath is the path to the `version.json` file in the Docker container. 18 | versionFilePath = "/app/version.json" 19 | ) 20 | 21 | func main() { 22 | app := cli.NewApp() 23 | app.Name = "bouncer" 24 | app.Action = Main 25 | app.Flags = []cli.Flag{ 26 | cli.IntFlag{ 27 | Name: "cache-time", 28 | Value: 60, 29 | Usage: "Time, in seconds, for Cache-Control max-age", 30 | }, 31 | cli.StringFlag{ 32 | Name: "addr", 33 | Value: ":8888", 34 | Usage: "Address on which to listen", 35 | EnvVar: "BOUNCER_ADDR", 36 | }, 37 | cli.StringFlag{ 38 | Name: "db-dsn", 39 | Value: "user:password@tcp(localhost:3306)/bouncer", 40 | Usage: "Database DSN (https://github.com/go-sql-driver/mysql#dsn-data-source-name)", 41 | EnvVar: "BOUNCER_DB_DSN", 42 | }, 43 | cli.StringFlag{ 44 | Name: "pin-https-header-name", 45 | Value: "X-Forwarded-Proto", 46 | Usage: "If this flag is set and the request header value equals https, an HTTPS redirect will always be returned", 47 | EnvVar: "BOUNCER_PIN_HTTPS_HEADER_NAME", 48 | }, 49 | cli.StringFlag{ 50 | Name: "pinned-baseurl-http", 51 | Usage: "The base URL for HTTP products. Scheme should be excluded, e.g. pinned-cdn.mozilla.com/pub", 52 | EnvVar: "BOUNCER_PINNED_BASEURL_HTTP", 53 | }, 54 | cli.StringFlag{ 55 | Name: "pinned-baseurl-https", 56 | Usage: "The base URL for HTTPS products. Scheme should be excluded, e.g. pinned-cdn.mozilla.com/pub", 57 | EnvVar: "BOUNCER_PINNED_BASEURL_HTTPS", 58 | }, 59 | cli.StringFlag{ 60 | Name: "stub-root-url", 61 | Value: "", 62 | Usage: "Optional. Root URL of the stubattribution service, e.g. https://stubdownloader.services.mozilla.com/", 63 | EnvVar: "BOUNCER_STUB_ROOT_URL", 64 | }, 65 | } 66 | if err := app.Run(os.Args); err != nil { 67 | log.Fatal(err) 68 | } 69 | } 70 | 71 | func versionHandler(w http.ResponseWriter, _ *http.Request) { 72 | versionFile, err := os.ReadFile(versionFilePath) 73 | if err != nil { 74 | http.Error(w, "Could not read version file.", http.StatusNotFound) 75 | return 76 | } 77 | 78 | w.Header().Set("Content-Type", "application/json") 79 | w.Write(versionFile) 80 | } 81 | 82 | // Main is the entrypoint of the application. 83 | func Main(c *cli.Context) { 84 | db, err := NewDB(c.String("db-dsn")) 85 | if err != nil { 86 | log.Fatalf("Could not open DB: %v", err) 87 | } 88 | defer db.Close() 89 | db.SetConnMaxLifetime(300 * time.Second) 90 | 91 | if c.String("pinned-baseurl-http") == "" { 92 | log.Fatal("BOUNCER_PINNED_BASEURL_HTTP must be set") 93 | } 94 | 95 | if c.String("pinned-baseurl-https") == "" { 96 | log.Fatal("BOUNCER_PINNED_BASEURL_HTTPS must be set") 97 | } 98 | 99 | bouncerHandler := &BouncerHandler{ 100 | db: db, 101 | CacheTime: time.Duration(c.Int("cache-time")) * time.Second, 102 | PinHTTPSHeaderName: c.String("pin-https-header-name"), 103 | PinnedBaseURLHttp: c.String("pinned-baseurl-http"), 104 | PinnedBaseURLHttps: c.String("pinned-baseurl-https"), 105 | StubRootURL: c.String("stub-root-url"), 106 | } 107 | 108 | healthHandler := &HealthHandler{ 109 | db: db, 110 | CacheTime: 5 * time.Second, 111 | } 112 | 113 | mux := http.NewServeMux() 114 | 115 | mux.Handle("/__lbheartbeat__", healthHandler) 116 | mux.Handle("/__heartbeat__", healthHandler) 117 | mux.HandleFunc("/__version__", versionHandler) 118 | mux.Handle("/", bouncerHandler) 119 | 120 | server := &http.Server{ 121 | Addr: c.String("addr"), 122 | Handler: mux, 123 | } 124 | 125 | err = server.ListenAndServe() 126 | if err != nil { 127 | log.Fatal(err) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /mozlog/mozlog.go: -------------------------------------------------------------------------------- 1 | // Package mozlog is an implementation of the mozlog logging format. 2 | package mozlog 3 | 4 | import ( 5 | "bytes" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | "time" 12 | ) 13 | 14 | var mozLogger = &MozLogger{ 15 | Output: os.Stdout, 16 | LoggerName: "Bouncer", 17 | } 18 | 19 | var hostname string 20 | 21 | // MozLogger implements the io.Writer interface 22 | type MozLogger struct { 23 | Output io.Writer 24 | LoggerName string 25 | } 26 | 27 | func init() { 28 | var err error 29 | hostname, err = os.Hostname() 30 | if err != nil { 31 | log.Printf("Can't resolve hostname: %v", err) 32 | } 33 | 34 | log.SetOutput(mozLogger) 35 | log.SetFlags(log.Lshortfile) 36 | } 37 | 38 | // Write converts the log to AppLog 39 | func (m *MozLogger) Write(l []byte) (int, error) { 40 | log := NewAppLog(m.LoggerName, l) 41 | 42 | out, err := log.ToJSON() 43 | if err != nil { 44 | // Need someway to notify that this happened. 45 | fmt.Fprintln(os.Stderr, err) 46 | return 0, err 47 | } 48 | 49 | _, err = m.Output.Write(append(out, '\n')) 50 | return len(l), err 51 | } 52 | 53 | // AppLog implements Mozilla logging standard 54 | type AppLog struct { 55 | Timestamp int64 56 | Type string 57 | Logger string 58 | Hostname string `json:",omitempty"` 59 | EnvVersion string 60 | Pid int `json:",omitempty"` 61 | Severity int `json:",omitempty"` 62 | Fields map[string]interface{} 63 | } 64 | 65 | // NewAppLog returns a loggable struct 66 | func NewAppLog(loggerName string, msg []byte) *AppLog { 67 | return &AppLog{ 68 | Timestamp: time.Now().UnixNano(), 69 | Type: "app.log", 70 | Logger: loggerName, 71 | Hostname: hostname, 72 | EnvVersion: "2.0", 73 | Pid: os.Getpid(), 74 | Fields: map[string]interface{}{ 75 | "msg": string(bytes.TrimSpace(msg)), 76 | }, 77 | } 78 | } 79 | 80 | // ToJSON converts a logline to JSON 81 | func (a *AppLog) ToJSON() ([]byte, error) { 82 | return json.Marshal(a) 83 | } 84 | -------------------------------------------------------------------------------- /mozlog/mozlog_test.go: -------------------------------------------------------------------------------- 1 | package mozlog 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "log" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestMozLogger(t *testing.T) { 13 | log.SetFlags(0) 14 | 15 | in := new(bytes.Buffer) 16 | mozLogger.Output = in 17 | 18 | log.Println("test message") 19 | 20 | var logEntry AppLog 21 | err := json.Unmarshal(in.Bytes(), &logEntry) 22 | assert.NoError(t, err) 23 | 24 | assert.Equal(t, "test message", logEntry.Fields["msg"]) 25 | } 26 | -------------------------------------------------------------------------------- /params.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | "strings" 7 | ) 8 | 9 | // BouncerParams holds/parses params for incoming bouncer requests 10 | type BouncerParams struct { 11 | PrintOnly bool 12 | OS string 13 | Product string 14 | Lang string 15 | AttributionCode string 16 | AttributionSig string 17 | Referer string 18 | } 19 | 20 | // BouncerParamsFromValues constructs parameter list from incoming request Values 21 | func BouncerParamsFromValues(vals url.Values, headers http.Header) *BouncerParams { 22 | return &BouncerParams{ 23 | PrintOnly: vals.Get("print") == "yes", 24 | OS: strings.TrimSpace(strings.ToLower(vals.Get("os"))), 25 | Product: strings.TrimSpace(strings.ToLower(vals.Get("product"))), 26 | Lang: vals.Get("lang"), 27 | AttributionCode: vals.Get("attribution_code"), 28 | AttributionSig: vals.Get("attribution_sig"), 29 | Referer: headers.Get("Referer"), 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/create_docker_testdb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | docker kill bouncer-test-mysql || true 6 | docker rm bouncer-test-mysql || true 7 | 8 | docker run -v "$PWD/docker/initdb.d/:/fixtures" -d --name bouncer-test-mysql \ 9 | -p 3306:3306 \ 10 | -e MYSQL_ALLOW_EMPTY_PASSWORD="yes" \ 11 | -e MYSQL_DATABASE="bouncer_test" \ 12 | mysql 13 | 14 | sleep 20 15 | 16 | docker exec bouncer-test-mysql sh -c "mysql bouncer_test < /fixtures/01-schema.sql" 17 | docker exec bouncer-test-mysql sh -c "mysql bouncer_test < /fixtures/02-data.sql" 18 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/README.md: -------------------------------------------------------------------------------- 1 | # filippo.io/edwards25519 2 | 3 | ``` 4 | import "filippo.io/edwards25519" 5 | ``` 6 | 7 | This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives. 8 | Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519). 9 | 10 | The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality. 11 | 12 | Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/internal/edwards25519`/`crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative. 13 | 14 | Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements. 15 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package edwards25519 implements group logic for the twisted Edwards curve 6 | // 7 | // -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2 8 | // 9 | // This is better known as the Edwards curve equivalent to Curve25519, and is 10 | // the curve used by the Ed25519 signature scheme. 11 | // 12 | // Most users don't need this package, and should instead use crypto/ed25519 for 13 | // signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or 14 | // github.com/gtank/ristretto255 for prime order group logic. 15 | // 16 | // However, developers who do need to interact with low-level edwards25519 17 | // operations can use this package, which is an extended version of 18 | // crypto/internal/edwards25519 from the standard library repackaged as 19 | // an importable module. 20 | package edwards25519 21 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_amd64.go: -------------------------------------------------------------------------------- 1 | // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. 2 | 3 | //go:build amd64 && gc && !purego 4 | // +build amd64,gc,!purego 5 | 6 | package field 7 | 8 | // feMul sets out = a * b. It works like feMulGeneric. 9 | // 10 | //go:noescape 11 | func feMul(out *Element, a *Element, b *Element) 12 | 13 | // feSquare sets out = a * a. It works like feSquareGeneric. 14 | // 15 | //go:noescape 16 | func feSquare(out *Element, a *Element) 17 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !amd64 || !gc || purego 6 | // +build !amd64 !gc purego 7 | 8 | package field 9 | 10 | func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } 11 | 12 | func feSquare(v, x *Element) { feSquareGeneric(v, x) } 13 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build arm64 && gc && !purego 6 | // +build arm64,gc,!purego 7 | 8 | package field 9 | 10 | //go:noescape 11 | func carryPropagate(v *Element) 12 | 13 | func (v *Element) carryPropagate() *Element { 14 | carryPropagate(v) 15 | return v 16 | } 17 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_arm64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build arm64 && gc && !purego 6 | 7 | #include "textflag.h" 8 | 9 | // carryPropagate works exactly like carryPropagateGeneric and uses the 10 | // same AND, ADD, and LSR+MADD instructions emitted by the compiler, but 11 | // avoids loading R0-R4 twice and uses LDP and STP. 12 | // 13 | // See https://golang.org/issues/43145 for the main compiler issue. 14 | // 15 | // func carryPropagate(v *Element) 16 | TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 17 | MOVD v+0(FP), R20 18 | 19 | LDP 0(R20), (R0, R1) 20 | LDP 16(R20), (R2, R3) 21 | MOVD 32(R20), R4 22 | 23 | AND $0x7ffffffffffff, R0, R10 24 | AND $0x7ffffffffffff, R1, R11 25 | AND $0x7ffffffffffff, R2, R12 26 | AND $0x7ffffffffffff, R3, R13 27 | AND $0x7ffffffffffff, R4, R14 28 | 29 | ADD R0>>51, R11, R11 30 | ADD R1>>51, R12, R12 31 | ADD R2>>51, R13, R13 32 | ADD R3>>51, R14, R14 33 | // R4>>51 * 19 + R10 -> R10 34 | LSR $51, R4, R21 35 | MOVD $19, R22 36 | MADD R22, R10, R21, R10 37 | 38 | STP (R10, R11), 0(R20) 39 | STP (R12, R13), 16(R20) 40 | MOVD R14, 32(R20) 41 | 42 | RET 43 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !arm64 || !gc || purego 6 | // +build !arm64 !gc purego 7 | 8 | package field 9 | 10 | func (v *Element) carryPropagate() *Element { 11 | return v.carryPropagateGeneric() 12 | } 13 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_extra.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package field 6 | 7 | import "errors" 8 | 9 | // This file contains additional functionality that is not included in the 10 | // upstream crypto/ed25519/edwards25519/field package. 11 | 12 | // SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which 13 | // is reduced modulo the field order. If x is not of the right length, 14 | // SetWideBytes returns nil and an error, and the receiver is unchanged. 15 | // 16 | // SetWideBytes is not necessary to select a uniformly distributed value, and is 17 | // only provided for compatibility: SetBytes can be used instead as the chance 18 | // of bias is less than 2⁻²⁵⁰. 19 | func (v *Element) SetWideBytes(x []byte) (*Element, error) { 20 | if len(x) != 64 { 21 | return nil, errors.New("edwards25519: invalid SetWideBytes input size") 22 | } 23 | 24 | // Split the 64 bytes into two elements, and extract the most significant 25 | // bit of each, which is ignored by SetBytes. 26 | lo, _ := new(Element).SetBytes(x[:32]) 27 | loMSB := uint64(x[31] >> 7) 28 | hi, _ := new(Element).SetBytes(x[32:]) 29 | hiMSB := uint64(x[63] >> 7) 30 | 31 | // The output we want is 32 | // 33 | // v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹ 34 | // 35 | // which applying the reduction identity comes out to 36 | // 37 | // v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19² 38 | // 39 | // l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value 40 | // (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value 41 | // (hiMSB * 2 * 19²), so it fits in a uint64. 42 | 43 | v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19 44 | v.l1 = lo.l1 + hi.l1*2*19 45 | v.l2 = lo.l2 + hi.l2*2*19 46 | v.l3 = lo.l3 + hi.l3*2*19 47 | v.l4 = lo.l4 + hi.l4*2*19 48 | 49 | return v.carryPropagate(), nil 50 | } 51 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/tables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package edwards25519 6 | 7 | import ( 8 | "crypto/subtle" 9 | ) 10 | 11 | // A dynamic lookup table for variable-base, constant-time scalar muls. 12 | type projLookupTable struct { 13 | points [8]projCached 14 | } 15 | 16 | // A precomputed lookup table for fixed-base, constant-time scalar muls. 17 | type affineLookupTable struct { 18 | points [8]affineCached 19 | } 20 | 21 | // A dynamic lookup table for variable-base, variable-time scalar muls. 22 | type nafLookupTable5 struct { 23 | points [8]projCached 24 | } 25 | 26 | // A precomputed lookup table for fixed-base, variable-time scalar muls. 27 | type nafLookupTable8 struct { 28 | points [64]affineCached 29 | } 30 | 31 | // Constructors. 32 | 33 | // Builds a lookup table at runtime. Fast. 34 | func (v *projLookupTable) FromP3(q *Point) { 35 | // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q 36 | // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q 37 | v.points[0].FromP3(q) 38 | tmpP3 := Point{} 39 | tmpP1xP1 := projP1xP1{} 40 | for i := 0; i < 7; i++ { 41 | // Compute (i+1)*Q as Q + i*Q and convert to a projCached 42 | // This is needlessly complicated because the API has explicit 43 | // receivers instead of creating stack objects and relying on RVO 44 | v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i]))) 45 | } 46 | } 47 | 48 | // This is not optimised for speed; fixed-base tables should be precomputed. 49 | func (v *affineLookupTable) FromP3(q *Point) { 50 | // Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q 51 | // This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q 52 | v.points[0].FromP3(q) 53 | tmpP3 := Point{} 54 | tmpP1xP1 := projP1xP1{} 55 | for i := 0; i < 7; i++ { 56 | // Compute (i+1)*Q as Q + i*Q and convert to affineCached 57 | v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i]))) 58 | } 59 | } 60 | 61 | // Builds a lookup table at runtime. Fast. 62 | func (v *nafLookupTable5) FromP3(q *Point) { 63 | // Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q 64 | // This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q 65 | v.points[0].FromP3(q) 66 | q2 := Point{} 67 | q2.Add(q, q) 68 | tmpP3 := Point{} 69 | tmpP1xP1 := projP1xP1{} 70 | for i := 0; i < 7; i++ { 71 | v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i]))) 72 | } 73 | } 74 | 75 | // This is not optimised for speed; fixed-base tables should be precomputed. 76 | func (v *nafLookupTable8) FromP3(q *Point) { 77 | v.points[0].FromP3(q) 78 | q2 := Point{} 79 | q2.Add(q, q) 80 | tmpP3 := Point{} 81 | tmpP1xP1 := projP1xP1{} 82 | for i := 0; i < 63; i++ { 83 | v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i]))) 84 | } 85 | } 86 | 87 | // Selectors. 88 | 89 | // Set dest to x*Q, where -8 <= x <= 8, in constant time. 90 | func (v *projLookupTable) SelectInto(dest *projCached, x int8) { 91 | // Compute xabs = |x| 92 | xmask := x >> 7 93 | xabs := uint8((x + xmask) ^ xmask) 94 | 95 | dest.Zero() 96 | for j := 1; j <= 8; j++ { 97 | // Set dest = j*Q if |x| = j 98 | cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) 99 | dest.Select(&v.points[j-1], dest, cond) 100 | } 101 | // Now dest = |x|*Q, conditionally negate to get x*Q 102 | dest.CondNeg(int(xmask & 1)) 103 | } 104 | 105 | // Set dest to x*Q, where -8 <= x <= 8, in constant time. 106 | func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) { 107 | // Compute xabs = |x| 108 | xmask := x >> 7 109 | xabs := uint8((x + xmask) ^ xmask) 110 | 111 | dest.Zero() 112 | for j := 1; j <= 8; j++ { 113 | // Set dest = j*Q if |x| = j 114 | cond := subtle.ConstantTimeByteEq(xabs, uint8(j)) 115 | dest.Select(&v.points[j-1], dest, cond) 116 | } 117 | // Now dest = |x|*Q, conditionally negate to get x*Q 118 | dest.CondNeg(int(xmask & 1)) 119 | } 120 | 121 | // Given odd x with 0 < x < 2^4, return x*Q (in variable time). 122 | func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) { 123 | *dest = v.points[x/2] 124 | } 125 | 126 | // Given odd x with 0 < x < 2^7, return x*Q (in variable time). 127 | func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) { 128 | *dest = v.points[x/2] 129 | } 130 | -------------------------------------------------------------------------------- /vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Brian Goff 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. 22 | -------------------------------------------------------------------------------- /vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go: -------------------------------------------------------------------------------- 1 | package md2man 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | "github.com/russross/blackfriday/v2" 10 | ) 11 | 12 | func fmtListFlags(flags blackfriday.ListType) string { 13 | knownFlags := []struct { 14 | name string 15 | flag blackfriday.ListType 16 | }{ 17 | {"ListTypeOrdered", blackfriday.ListTypeOrdered}, 18 | {"ListTypeDefinition", blackfriday.ListTypeDefinition}, 19 | {"ListTypeTerm", blackfriday.ListTypeTerm}, 20 | {"ListItemContainsBlock", blackfriday.ListItemContainsBlock}, 21 | {"ListItemBeginningOfList", blackfriday.ListItemBeginningOfList}, 22 | {"ListItemEndOfList", blackfriday.ListItemEndOfList}, 23 | } 24 | 25 | var f []string 26 | for _, kf := range knownFlags { 27 | if flags&kf.flag != 0 { 28 | f = append(f, kf.name) 29 | flags &^= kf.flag 30 | } 31 | } 32 | if flags != 0 { 33 | f = append(f, fmt.Sprintf("Unknown(%#x)", flags)) 34 | } 35 | return strings.Join(f, "|") 36 | } 37 | 38 | type debugDecorator struct { 39 | blackfriday.Renderer 40 | } 41 | 42 | func depth(node *blackfriday.Node) int { 43 | d := 0 44 | for n := node.Parent; n != nil; n = n.Parent { 45 | d++ 46 | } 47 | return d 48 | } 49 | 50 | func (d *debugDecorator) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { 51 | fmt.Fprintf(os.Stderr, "%s%s %v %v\n", 52 | strings.Repeat(" ", depth(node)), 53 | map[bool]string{true: "+", false: "-"}[entering], 54 | node, 55 | fmtListFlags(node.ListFlags)) 56 | var b strings.Builder 57 | status := d.Renderer.RenderNode(io.MultiWriter(&b, w), node, entering) 58 | if b.Len() > 0 { 59 | fmt.Fprintf(os.Stderr, ">> %q\n", b.String()) 60 | } 61 | return status 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go: -------------------------------------------------------------------------------- 1 | // Package md2man aims in converting markdown into roff (man pages). 2 | package md2man 3 | 4 | import ( 5 | "os" 6 | "strconv" 7 | 8 | "github.com/russross/blackfriday/v2" 9 | ) 10 | 11 | // Render converts a markdown document into a roff formatted document. 12 | func Render(doc []byte) []byte { 13 | renderer := NewRoffRenderer() 14 | var r blackfriday.Renderer = renderer 15 | if v, _ := strconv.ParseBool(os.Getenv("MD2MAN_DEBUG")); v { 16 | r = &debugDecorator{Renderer: r} 17 | } 18 | 19 | return blackfriday.Run(doc, 20 | []blackfriday.Option{ 21 | blackfriday.WithRenderer(r), 22 | blackfriday.WithExtensions(renderer.GetExtensions()), 23 | }...) 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // Go versions prior to 1.4 are disabled because they use a different layout 20 | // for interfaces which make the implementation of unsafeReflectValue more complex. 21 | // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 | 23 | package spew 24 | 25 | import ( 26 | "reflect" 27 | "unsafe" 28 | ) 29 | 30 | const ( 31 | // UnsafeDisabled is a build-time constant which specifies whether or 32 | // not access to the unsafe package is available. 33 | UnsafeDisabled = false 34 | 35 | // ptrSize is the size of a pointer on the current arch. 36 | ptrSize = unsafe.Sizeof((*byte)(nil)) 37 | ) 38 | 39 | type flag uintptr 40 | 41 | var ( 42 | // flagRO indicates whether the value field of a reflect.Value 43 | // is read-only. 44 | flagRO flag 45 | 46 | // flagAddr indicates whether the address of the reflect.Value's 47 | // value may be taken. 48 | flagAddr flag 49 | ) 50 | 51 | // flagKindMask holds the bits that make up the kind 52 | // part of the flags field. In all the supported versions, 53 | // it is in the lower 5 bits. 54 | const flagKindMask = flag(0x1f) 55 | 56 | // Different versions of Go have used different 57 | // bit layouts for the flags type. This table 58 | // records the known combinations. 59 | var okFlags = []struct { 60 | ro, addr flag 61 | }{{ 62 | // From Go 1.4 to 1.5 63 | ro: 1 << 5, 64 | addr: 1 << 7, 65 | }, { 66 | // Up to Go tip. 67 | ro: 1<<5 | 1<<6, 68 | addr: 1 << 8, 69 | }} 70 | 71 | var flagValOffset = func() uintptr { 72 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 | if !ok { 74 | panic("reflect.Value has no flag field") 75 | } 76 | return field.Offset 77 | }() 78 | 79 | // flagField returns a pointer to the flag field of a reflect.Value. 80 | func flagField(v *reflect.Value) *flag { 81 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 | } 83 | 84 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 | // the typical safety restrictions preventing access to unaddressable and 86 | // unexported data. It works by digging the raw pointer to the underlying 87 | // value out of the protected value and generating a new unprotected (unsafe) 88 | // reflect.Value to it. 89 | // 90 | // This allows us to check for implementations of the Stringer and error 91 | // interfaces to be used for pretty printing ordinarily unaddressable and 92 | // inaccessible values such as unexported struct fields. 93 | func unsafeReflectValue(v reflect.Value) reflect.Value { 94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 | return v 96 | } 97 | flagFieldPtr := flagField(&v) 98 | *flagFieldPtr &^= flagRO 99 | *flagFieldPtr |= flagAddr 100 | return v 101 | } 102 | 103 | // Sanity checks against future reflect package changes 104 | // to the type or semantics of the Value.flag field. 105 | func init() { 106 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 | if !ok { 108 | panic("reflect.Value has no flag field") 109 | } 110 | if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 | panic("reflect.Value flag field has changed kind") 112 | } 113 | type t0 int 114 | var t struct { 115 | A t0 116 | // t0 will have flagEmbedRO set. 117 | t0 118 | // a will have flagStickyRO set 119 | a t0 120 | } 121 | vA := reflect.ValueOf(t).FieldByName("A") 122 | va := reflect.ValueOf(t).FieldByName("a") 123 | vt0 := reflect.ValueOf(t).FieldByName("t0") 124 | 125 | // Infer flagRO from the difference between the flags 126 | // for the (otherwise identical) fields in t. 127 | flagPublic := *flagField(&vA) 128 | flagWithRO := *flagField(&va) | *flagField(&vt0) 129 | flagRO = flagPublic ^ flagWithRO 130 | 131 | // Infer flagAddr from the difference between a value 132 | // taken from a pointer and not. 133 | vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 | flagNoPtr := *flagField(&vA) 135 | flagPtr := *flagField(&vPtrA) 136 | flagAddr = flagNoPtr ^ flagPtr 137 | 138 | // Check that the inferred flags tally with one of the known versions. 139 | for _, f := range okFlags { 140 | if flagRO == f.ro && flagAddr == f.addr { 141 | return 142 | } 143 | } 144 | panic("reflect.Value read-only flag has changed semantics") 145 | } 146 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe !go1.4 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ._* 4 | .Spotlight-V100 5 | .Trashes 6 | Icon? 7 | ehthumbs.db 8 | Thumbs.db 9 | .idea 10 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go-MySQL-Driver authors for copyright purposes. 2 | 3 | # If you are submitting a patch, please add your name or the name of the 4 | # organization which holds the copyright to this list in alphabetical order. 5 | 6 | # Names should be added to this file as 7 | # Name 8 | # The email address is not required for organizations. 9 | # Please keep the list sorted. 10 | 11 | 12 | # Individual Persons 13 | 14 | Aaron Hopkins 15 | Achille Roussel 16 | Aidan 17 | Alex Snast 18 | Alexey Palazhchenko 19 | Andrew Reid 20 | Animesh Ray 21 | Arne Hormann 22 | Ariel Mashraki 23 | Artur Melanchyk 24 | Asta Xie 25 | B Lamarche 26 | Bes Dollma 27 | Bogdan Constantinescu 28 | Brad Higgins 29 | Brian Hendriks 30 | Bulat Gaifullin 31 | Caine Jette 32 | Carlos Nieto 33 | Chris Kirkland 34 | Chris Moos 35 | Craig Wilson 36 | Daemonxiao <735462752 at qq.com> 37 | Daniel Montoya 38 | Daniel Nichter 39 | Daniël van Eeden 40 | Dave Protasowski 41 | Diego Dupin 42 | Dirkjan Bussink 43 | DisposaBoy 44 | Egor Smolyakov 45 | Erwan Martin 46 | Evan Elias 47 | Evan Shaw 48 | Frederick Mayle 49 | Gustavo Kristic 50 | Gusted 51 | Hajime Nakagami 52 | Hanno Braun 53 | Henri Yandell 54 | Hirotaka Yamamoto 55 | Huyiguang 56 | ICHINOSE Shogo 57 | Ilia Cimpoes 58 | INADA Naoki 59 | Jacek Szwec 60 | Jakub Adamus 61 | James Harr 62 | Janek Vedock 63 | Jason Ng 64 | Jean-Yves Pellé 65 | Jeff Hodges 66 | Jeffrey Charles 67 | Jennifer Purevsuren 68 | Jerome Meyer 69 | Jiajia Zhong 70 | Jian Zhen 71 | Joe Mann 72 | Joshua Prunier 73 | Julien Lefevre 74 | Julien Schmidt 75 | Justin Li 76 | Justin Nuß 77 | Kamil Dziedzic 78 | Kei Kamikawa 79 | Kevin Malachowski 80 | Kieron Woodhouse 81 | Lance Tian 82 | Lennart Rudolph 83 | Leonardo YongUk Kim 84 | Linh Tran Tuan 85 | Lion Yang 86 | Luca Looz 87 | Lucas Liu 88 | Lunny Xiao 89 | Luke Scott 90 | Maciej Zimnoch 91 | Michael Woolnough 92 | Nao Yokotsuka 93 | Nathanial Murphy 94 | Nicola Peduzzi 95 | Oliver Bone 96 | Olivier Mengué 97 | oscarzhao 98 | Paul Bonser 99 | Paulius Lozys 100 | Peter Schultz 101 | Phil Porada 102 | Minh Quang 103 | Rebecca Chin 104 | Reed Allman 105 | Richard Wilkes 106 | Robert Russell 107 | Runrioter Wung 108 | Samantha Frank 109 | Santhosh Kumar Tekuri 110 | Sho Iizuka 111 | Sho Ikeda 112 | Shuode Li 113 | Simon J Mudd 114 | Soroush Pour 115 | Stan Putrya 116 | Stanley Gunawan 117 | Steven Hartland 118 | Tan Jinhua <312841925 at qq.com> 119 | Tetsuro Aoki 120 | Thomas Wodarek 121 | Tim Ruffles 122 | Tom Jenkinson 123 | Vladimir Kovpak 124 | Vladyslav Zhelezniak 125 | Xiangyu Hu 126 | Xiaobing Jiang 127 | Xiuming Chen 128 | Xuehong Chan 129 | Zhang Xiang 130 | Zhenye Xie 131 | Zhixin Wen 132 | Ziheng Lyu 133 | 134 | # Organizations 135 | 136 | Barracuda Networks, Inc. 137 | Counting Ltd. 138 | Defined Networking Inc. 139 | DigitalOcean Inc. 140 | Dolthub Inc. 141 | dyves labs AG 142 | Facebook Inc. 143 | GitHub Inc. 144 | Google Inc. 145 | InfoSum Ltd. 146 | Keybase Inc. 147 | Microsoft Corp. 148 | Multiplay Ltd. 149 | Percona LLC 150 | PingCAP Inc. 151 | Pivotal Inc. 152 | Shattered Silicon Ltd. 153 | Stripe Inc. 154 | ThousandEyes 155 | Zendesk Inc. 156 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/buffer.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "io" 13 | ) 14 | 15 | const defaultBufSize = 4096 16 | const maxCachedBufSize = 256 * 1024 17 | 18 | // readerFunc is a function that compatible with io.Reader. 19 | // We use this function type instead of io.Reader because we want to 20 | // just pass mc.readWithTimeout. 21 | type readerFunc func([]byte) (int, error) 22 | 23 | // A buffer which is used for both reading and writing. 24 | // This is possible since communication on each connection is synchronous. 25 | // In other words, we can't write and read simultaneously on the same connection. 26 | // The buffer is similar to bufio.Reader / Writer but zero-copy-ish 27 | // Also highly optimized for this particular use case. 28 | type buffer struct { 29 | buf []byte // read buffer. 30 | cachedBuf []byte // buffer that will be reused. len(cachedBuf) <= maxCachedBufSize. 31 | } 32 | 33 | // newBuffer allocates and returns a new buffer. 34 | func newBuffer() buffer { 35 | return buffer{ 36 | cachedBuf: make([]byte, defaultBufSize), 37 | } 38 | } 39 | 40 | // busy returns true if the read buffer is not empty. 41 | func (b *buffer) busy() bool { 42 | return len(b.buf) > 0 43 | } 44 | 45 | // len returns how many bytes in the read buffer. 46 | func (b *buffer) len() int { 47 | return len(b.buf) 48 | } 49 | 50 | // fill reads into the read buffer until at least _need_ bytes are in it. 51 | func (b *buffer) fill(need int, r readerFunc) error { 52 | // we'll move the contents of the current buffer to dest before filling it. 53 | dest := b.cachedBuf 54 | 55 | // grow buffer if necessary to fit the whole packet. 56 | if need > len(dest) { 57 | // Round up to the next multiple of the default size 58 | dest = make([]byte, ((need/defaultBufSize)+1)*defaultBufSize) 59 | 60 | // if the allocated buffer is not too large, move it to backing storage 61 | // to prevent extra allocations on applications that perform large reads 62 | if len(dest) <= maxCachedBufSize { 63 | b.cachedBuf = dest 64 | } 65 | } 66 | 67 | // move the existing data to the start of the buffer. 68 | n := len(b.buf) 69 | copy(dest[:n], b.buf) 70 | 71 | for { 72 | nn, err := r(dest[n:]) 73 | n += nn 74 | 75 | if err == nil && n < need { 76 | continue 77 | } 78 | 79 | b.buf = dest[:n] 80 | 81 | if err == io.EOF { 82 | if n < need { 83 | err = io.ErrUnexpectedEOF 84 | } else { 85 | err = nil 86 | } 87 | } 88 | return err 89 | } 90 | } 91 | 92 | // returns next N bytes from buffer. 93 | // The returned slice is only guaranteed to be valid until the next read 94 | func (b *buffer) readNext(need int) []byte { 95 | data := b.buf[:need:need] 96 | b.buf = b.buf[need:] 97 | return data 98 | } 99 | 100 | // takeBuffer returns a buffer with the requested size. 101 | // If possible, a slice from the existing buffer is returned. 102 | // Otherwise a bigger buffer is made. 103 | // Only one buffer (total) can be used at a time. 104 | func (b *buffer) takeBuffer(length int) ([]byte, error) { 105 | if b.busy() { 106 | return nil, ErrBusyBuffer 107 | } 108 | 109 | // test (cheap) general case first 110 | if length <= len(b.cachedBuf) { 111 | return b.cachedBuf[:length], nil 112 | } 113 | 114 | if length < maxCachedBufSize { 115 | b.cachedBuf = make([]byte, length) 116 | return b.cachedBuf, nil 117 | } 118 | 119 | // buffer is larger than we want to store. 120 | return make([]byte, length), nil 121 | } 122 | 123 | // takeSmallBuffer is shortcut which can be used if length is 124 | // known to be smaller than defaultBufSize. 125 | // Only one buffer (total) can be used at a time. 126 | func (b *buffer) takeSmallBuffer(length int) ([]byte, error) { 127 | if b.busy() { 128 | return nil, ErrBusyBuffer 129 | } 130 | return b.cachedBuf[:length], nil 131 | } 132 | 133 | // takeCompleteBuffer returns the complete existing buffer. 134 | // This can be used if the necessary buffer size is unknown. 135 | // cap and len of the returned buffer will be equal. 136 | // Only one buffer (total) can be used at a time. 137 | func (b *buffer) takeCompleteBuffer() ([]byte, error) { 138 | if b.busy() { 139 | return nil, ErrBusyBuffer 140 | } 141 | return b.cachedBuf, nil 142 | } 143 | 144 | // store stores buf, an updated buffer, if its suitable to do so. 145 | func (b *buffer) store(buf []byte) { 146 | if cap(buf) <= maxCachedBufSize && cap(buf) > cap(b.cachedBuf) { 147 | b.cachedBuf = buf[:cap(buf)] 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/conncheck.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2019 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | //go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos 10 | // +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos 11 | 12 | package mysql 13 | 14 | import ( 15 | "errors" 16 | "io" 17 | "net" 18 | "syscall" 19 | ) 20 | 21 | var errUnexpectedRead = errors.New("unexpected read from socket") 22 | 23 | func connCheck(conn net.Conn) error { 24 | var sysErr error 25 | 26 | sysConn, ok := conn.(syscall.Conn) 27 | if !ok { 28 | return nil 29 | } 30 | rawConn, err := sysConn.SyscallConn() 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = rawConn.Read(func(fd uintptr) bool { 36 | var buf [1]byte 37 | n, err := syscall.Read(int(fd), buf[:]) 38 | switch { 39 | case n == 0 && err == nil: 40 | sysErr = io.EOF 41 | case n > 0: 42 | sysErr = errUnexpectedRead 43 | case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK: 44 | sysErr = nil 45 | default: 46 | sysErr = err 47 | } 48 | return true 49 | }) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return sysErr 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2019 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | //go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos 10 | // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos 11 | 12 | package mysql 13 | 14 | import "net" 15 | 16 | func connCheck(conn net.Conn) error { 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/const.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import "runtime" 12 | 13 | const ( 14 | debug = false // for debugging. Set true only in development. 15 | 16 | defaultAuthPlugin = "mysql_native_password" 17 | defaultMaxAllowedPacket = 64 << 20 // 64 MiB. See https://github.com/go-sql-driver/mysql/issues/1355 18 | minProtocolVersion = 10 19 | maxPacketSize = 1<<24 - 1 20 | timeFormat = "2006-01-02 15:04:05.999999" 21 | 22 | // Connection attributes 23 | // See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html#performance-schema-connection-attributes-available 24 | connAttrClientName = "_client_name" 25 | connAttrClientNameValue = "Go-MySQL-Driver" 26 | connAttrOS = "_os" 27 | connAttrOSValue = runtime.GOOS 28 | connAttrPlatform = "_platform" 29 | connAttrPlatformValue = runtime.GOARCH 30 | connAttrPid = "_pid" 31 | connAttrServerHost = "_server_host" 32 | ) 33 | 34 | // MySQL constants documentation: 35 | // http://dev.mysql.com/doc/internals/en/client-server-protocol.html 36 | 37 | const ( 38 | iOK byte = 0x00 39 | iAuthMoreData byte = 0x01 40 | iLocalInFile byte = 0xfb 41 | iEOF byte = 0xfe 42 | iERR byte = 0xff 43 | ) 44 | 45 | // https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags 46 | type clientFlag uint32 47 | 48 | const ( 49 | clientLongPassword clientFlag = 1 << iota 50 | clientFoundRows 51 | clientLongFlag 52 | clientConnectWithDB 53 | clientNoSchema 54 | clientCompress 55 | clientODBC 56 | clientLocalFiles 57 | clientIgnoreSpace 58 | clientProtocol41 59 | clientInteractive 60 | clientSSL 61 | clientIgnoreSIGPIPE 62 | clientTransactions 63 | clientReserved 64 | clientSecureConn 65 | clientMultiStatements 66 | clientMultiResults 67 | clientPSMultiResults 68 | clientPluginAuth 69 | clientConnectAttrs 70 | clientPluginAuthLenEncClientData 71 | clientCanHandleExpiredPasswords 72 | clientSessionTrack 73 | clientDeprecateEOF 74 | ) 75 | 76 | const ( 77 | comQuit byte = iota + 1 78 | comInitDB 79 | comQuery 80 | comFieldList 81 | comCreateDB 82 | comDropDB 83 | comRefresh 84 | comShutdown 85 | comStatistics 86 | comProcessInfo 87 | comConnect 88 | comProcessKill 89 | comDebug 90 | comPing 91 | comTime 92 | comDelayedInsert 93 | comChangeUser 94 | comBinlogDump 95 | comTableDump 96 | comConnectOut 97 | comRegisterSlave 98 | comStmtPrepare 99 | comStmtExecute 100 | comStmtSendLongData 101 | comStmtClose 102 | comStmtReset 103 | comSetOption 104 | comStmtFetch 105 | ) 106 | 107 | // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType 108 | type fieldType byte 109 | 110 | const ( 111 | fieldTypeDecimal fieldType = iota 112 | fieldTypeTiny 113 | fieldTypeShort 114 | fieldTypeLong 115 | fieldTypeFloat 116 | fieldTypeDouble 117 | fieldTypeNULL 118 | fieldTypeTimestamp 119 | fieldTypeLongLong 120 | fieldTypeInt24 121 | fieldTypeDate 122 | fieldTypeTime 123 | fieldTypeDateTime 124 | fieldTypeYear 125 | fieldTypeNewDate 126 | fieldTypeVarChar 127 | fieldTypeBit 128 | ) 129 | const ( 130 | fieldTypeVector fieldType = iota + 0xf2 131 | fieldTypeInvalid 132 | fieldTypeBool 133 | fieldTypeJSON 134 | fieldTypeNewDecimal 135 | fieldTypeEnum 136 | fieldTypeSet 137 | fieldTypeTinyBLOB 138 | fieldTypeMediumBLOB 139 | fieldTypeLongBLOB 140 | fieldTypeBLOB 141 | fieldTypeVarString 142 | fieldTypeString 143 | fieldTypeGeometry 144 | ) 145 | 146 | type fieldFlag uint16 147 | 148 | const ( 149 | flagNotNULL fieldFlag = 1 << iota 150 | flagPriKey 151 | flagUniqueKey 152 | flagMultipleKey 153 | flagBLOB 154 | flagUnsigned 155 | flagZeroFill 156 | flagBinary 157 | flagEnum 158 | flagAutoIncrement 159 | flagTimestamp 160 | flagSet 161 | flagUnknown1 162 | flagUnknown2 163 | flagUnknown3 164 | flagUnknown4 165 | ) 166 | 167 | // http://dev.mysql.com/doc/internals/en/status-flags.html 168 | type statusFlag uint16 169 | 170 | const ( 171 | statusInTrans statusFlag = 1 << iota 172 | statusInAutocommit 173 | statusReserved // Not in documentation 174 | statusMoreResultsExists 175 | statusNoGoodIndexUsed 176 | statusNoIndexUsed 177 | statusCursorExists 178 | statusLastRowSent 179 | statusDbDropped 180 | statusNoBackslashEscapes 181 | statusMetadataChanged 182 | statusQueryWasSlow 183 | statusPsOutParams 184 | statusInTransReadonly 185 | statusSessionStateChanged 186 | ) 187 | 188 | const ( 189 | cachingSha2PasswordRequestPublicKey = 2 190 | cachingSha2PasswordFastAuthSuccess = 3 191 | cachingSha2PasswordPerformFullAuthentication = 4 192 | ) 193 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 | // You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | // Package mysql provides a MySQL driver for Go's database/sql package. 8 | // 9 | // The driver should be used via the database/sql package: 10 | // 11 | // import "database/sql" 12 | // import _ "github.com/go-sql-driver/mysql" 13 | // 14 | // db, err := sql.Open("mysql", "user:password@/dbname") 15 | // 16 | // See https://github.com/go-sql-driver/mysql#usage for details 17 | package mysql 18 | 19 | import ( 20 | "context" 21 | "database/sql" 22 | "database/sql/driver" 23 | "net" 24 | "sync" 25 | ) 26 | 27 | // MySQLDriver is exported to make the driver directly accessible. 28 | // In general the driver is used via the database/sql package. 29 | type MySQLDriver struct{} 30 | 31 | // DialFunc is a function which can be used to establish the network connection. 32 | // Custom dial functions must be registered with RegisterDial 33 | // 34 | // Deprecated: users should register a DialContextFunc instead 35 | type DialFunc func(addr string) (net.Conn, error) 36 | 37 | // DialContextFunc is a function which can be used to establish the network connection. 38 | // Custom dial functions must be registered with RegisterDialContext 39 | type DialContextFunc func(ctx context.Context, addr string) (net.Conn, error) 40 | 41 | var ( 42 | dialsLock sync.RWMutex 43 | dials map[string]DialContextFunc 44 | ) 45 | 46 | // RegisterDialContext registers a custom dial function. It can then be used by the 47 | // network address mynet(addr), where mynet is the registered new network. 48 | // The current context for the connection and its address is passed to the dial function. 49 | func RegisterDialContext(net string, dial DialContextFunc) { 50 | dialsLock.Lock() 51 | defer dialsLock.Unlock() 52 | if dials == nil { 53 | dials = make(map[string]DialContextFunc) 54 | } 55 | dials[net] = dial 56 | } 57 | 58 | // DeregisterDialContext removes the custom dial function registered with the given net. 59 | func DeregisterDialContext(net string) { 60 | dialsLock.Lock() 61 | defer dialsLock.Unlock() 62 | if dials != nil { 63 | delete(dials, net) 64 | } 65 | } 66 | 67 | // RegisterDial registers a custom dial function. It can then be used by the 68 | // network address mynet(addr), where mynet is the registered new network. 69 | // addr is passed as a parameter to the dial function. 70 | // 71 | // Deprecated: users should call RegisterDialContext instead 72 | func RegisterDial(network string, dial DialFunc) { 73 | RegisterDialContext(network, func(_ context.Context, addr string) (net.Conn, error) { 74 | return dial(addr) 75 | }) 76 | } 77 | 78 | // Open new Connection. 79 | // See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how 80 | // the DSN string is formatted 81 | func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { 82 | cfg, err := ParseDSN(dsn) 83 | if err != nil { 84 | return nil, err 85 | } 86 | c := newConnector(cfg) 87 | return c.Connect(context.Background()) 88 | } 89 | 90 | // This variable can be replaced with -ldflags like below: 91 | // go build "-ldflags=-X github.com/go-sql-driver/mysql.driverName=custom" 92 | var driverName = "mysql" 93 | 94 | func init() { 95 | if driverName != "" { 96 | sql.Register(driverName, &MySQLDriver{}) 97 | } 98 | } 99 | 100 | // NewConnector returns new driver.Connector. 101 | func NewConnector(cfg *Config) (driver.Connector, error) { 102 | cfg = cfg.Clone() 103 | // normalize the contents of cfg so calls to NewConnector have the same 104 | // behavior as MySQLDriver.OpenConnector 105 | if err := cfg.normalize(); err != nil { 106 | return nil, err 107 | } 108 | return newConnector(cfg), nil 109 | } 110 | 111 | // OpenConnector implements driver.DriverContext. 112 | func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) { 113 | cfg, err := ParseDSN(dsn) 114 | if err != nil { 115 | return nil, err 116 | } 117 | return newConnector(cfg), nil 118 | } 119 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/errors.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "errors" 13 | "fmt" 14 | "log" 15 | "os" 16 | ) 17 | 18 | // Various errors the driver might return. Can change between driver versions. 19 | var ( 20 | ErrInvalidConn = errors.New("invalid connection") 21 | ErrMalformPkt = errors.New("malformed packet") 22 | ErrNoTLS = errors.New("TLS requested but server does not support TLS") 23 | ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN") 24 | ErrNativePassword = errors.New("this user requires mysql native password authentication") 25 | ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") 26 | ErrUnknownPlugin = errors.New("this authentication plugin is not supported") 27 | ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") 28 | ErrPktSync = errors.New("commands out of sync. You can't run this command now") 29 | ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") 30 | ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`") 31 | ErrBusyBuffer = errors.New("busy buffer") 32 | 33 | // errBadConnNoWrite is used for connection errors where nothing was sent to the database yet. 34 | // If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn 35 | // to trigger a resend. Use mc.markBadConn(err) to do this. 36 | // See https://github.com/go-sql-driver/mysql/pull/302 37 | errBadConnNoWrite = errors.New("bad connection") 38 | ) 39 | 40 | var defaultLogger = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime)) 41 | 42 | // Logger is used to log critical error messages. 43 | type Logger interface { 44 | Print(v ...any) 45 | } 46 | 47 | // NopLogger is a nop implementation of the Logger interface. 48 | type NopLogger struct{} 49 | 50 | // Print implements Logger interface. 51 | func (nl *NopLogger) Print(_ ...any) {} 52 | 53 | // SetLogger is used to set the default logger for critical errors. 54 | // The initial logger is os.Stderr. 55 | func SetLogger(logger Logger) error { 56 | if logger == nil { 57 | return errors.New("logger is nil") 58 | } 59 | defaultLogger = logger 60 | return nil 61 | } 62 | 63 | // MySQLError is an error type which represents a single MySQL error 64 | type MySQLError struct { 65 | Number uint16 66 | SQLState [5]byte 67 | Message string 68 | } 69 | 70 | func (me *MySQLError) Error() string { 71 | if me.SQLState != [5]byte{} { 72 | return fmt.Sprintf("Error %d (%s): %s", me.Number, me.SQLState, me.Message) 73 | } 74 | 75 | return fmt.Sprintf("Error %d: %s", me.Number, me.Message) 76 | } 77 | 78 | func (me *MySQLError) Is(err error) bool { 79 | if merr, ok := err.(*MySQLError); ok { 80 | return merr.Number == me.Number 81 | } 82 | return false 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/fields.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql" 13 | "reflect" 14 | ) 15 | 16 | func (mf *mysqlField) typeDatabaseName() string { 17 | switch mf.fieldType { 18 | case fieldTypeBit: 19 | return "BIT" 20 | case fieldTypeBLOB: 21 | if mf.charSet != binaryCollationID { 22 | return "TEXT" 23 | } 24 | return "BLOB" 25 | case fieldTypeDate: 26 | return "DATE" 27 | case fieldTypeDateTime: 28 | return "DATETIME" 29 | case fieldTypeDecimal: 30 | return "DECIMAL" 31 | case fieldTypeDouble: 32 | return "DOUBLE" 33 | case fieldTypeEnum: 34 | return "ENUM" 35 | case fieldTypeFloat: 36 | return "FLOAT" 37 | case fieldTypeGeometry: 38 | return "GEOMETRY" 39 | case fieldTypeInt24: 40 | if mf.flags&flagUnsigned != 0 { 41 | return "UNSIGNED MEDIUMINT" 42 | } 43 | return "MEDIUMINT" 44 | case fieldTypeJSON: 45 | return "JSON" 46 | case fieldTypeLong: 47 | if mf.flags&flagUnsigned != 0 { 48 | return "UNSIGNED INT" 49 | } 50 | return "INT" 51 | case fieldTypeLongBLOB: 52 | if mf.charSet != binaryCollationID { 53 | return "LONGTEXT" 54 | } 55 | return "LONGBLOB" 56 | case fieldTypeLongLong: 57 | if mf.flags&flagUnsigned != 0 { 58 | return "UNSIGNED BIGINT" 59 | } 60 | return "BIGINT" 61 | case fieldTypeMediumBLOB: 62 | if mf.charSet != binaryCollationID { 63 | return "MEDIUMTEXT" 64 | } 65 | return "MEDIUMBLOB" 66 | case fieldTypeNewDate: 67 | return "DATE" 68 | case fieldTypeNewDecimal: 69 | return "DECIMAL" 70 | case fieldTypeNULL: 71 | return "NULL" 72 | case fieldTypeSet: 73 | return "SET" 74 | case fieldTypeShort: 75 | if mf.flags&flagUnsigned != 0 { 76 | return "UNSIGNED SMALLINT" 77 | } 78 | return "SMALLINT" 79 | case fieldTypeString: 80 | if mf.flags&flagEnum != 0 { 81 | return "ENUM" 82 | } else if mf.flags&flagSet != 0 { 83 | return "SET" 84 | } 85 | if mf.charSet == binaryCollationID { 86 | return "BINARY" 87 | } 88 | return "CHAR" 89 | case fieldTypeTime: 90 | return "TIME" 91 | case fieldTypeTimestamp: 92 | return "TIMESTAMP" 93 | case fieldTypeTiny: 94 | if mf.flags&flagUnsigned != 0 { 95 | return "UNSIGNED TINYINT" 96 | } 97 | return "TINYINT" 98 | case fieldTypeTinyBLOB: 99 | if mf.charSet != binaryCollationID { 100 | return "TINYTEXT" 101 | } 102 | return "TINYBLOB" 103 | case fieldTypeVarChar: 104 | if mf.charSet == binaryCollationID { 105 | return "VARBINARY" 106 | } 107 | return "VARCHAR" 108 | case fieldTypeVarString: 109 | if mf.charSet == binaryCollationID { 110 | return "VARBINARY" 111 | } 112 | return "VARCHAR" 113 | case fieldTypeYear: 114 | return "YEAR" 115 | case fieldTypeVector: 116 | return "VECTOR" 117 | default: 118 | return "" 119 | } 120 | } 121 | 122 | var ( 123 | scanTypeFloat32 = reflect.TypeOf(float32(0)) 124 | scanTypeFloat64 = reflect.TypeOf(float64(0)) 125 | scanTypeInt8 = reflect.TypeOf(int8(0)) 126 | scanTypeInt16 = reflect.TypeOf(int16(0)) 127 | scanTypeInt32 = reflect.TypeOf(int32(0)) 128 | scanTypeInt64 = reflect.TypeOf(int64(0)) 129 | scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) 130 | scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) 131 | scanTypeNullTime = reflect.TypeOf(sql.NullTime{}) 132 | scanTypeUint8 = reflect.TypeOf(uint8(0)) 133 | scanTypeUint16 = reflect.TypeOf(uint16(0)) 134 | scanTypeUint32 = reflect.TypeOf(uint32(0)) 135 | scanTypeUint64 = reflect.TypeOf(uint64(0)) 136 | scanTypeString = reflect.TypeOf("") 137 | scanTypeNullString = reflect.TypeOf(sql.NullString{}) 138 | scanTypeBytes = reflect.TypeOf([]byte{}) 139 | scanTypeUnknown = reflect.TypeOf(new(any)) 140 | ) 141 | 142 | type mysqlField struct { 143 | tableName string 144 | name string 145 | length uint32 146 | flags fieldFlag 147 | fieldType fieldType 148 | decimals byte 149 | charSet uint8 150 | } 151 | 152 | func (mf *mysqlField) scanType() reflect.Type { 153 | switch mf.fieldType { 154 | case fieldTypeTiny: 155 | if mf.flags&flagNotNULL != 0 { 156 | if mf.flags&flagUnsigned != 0 { 157 | return scanTypeUint8 158 | } 159 | return scanTypeInt8 160 | } 161 | return scanTypeNullInt 162 | 163 | case fieldTypeShort, fieldTypeYear: 164 | if mf.flags&flagNotNULL != 0 { 165 | if mf.flags&flagUnsigned != 0 { 166 | return scanTypeUint16 167 | } 168 | return scanTypeInt16 169 | } 170 | return scanTypeNullInt 171 | 172 | case fieldTypeInt24, fieldTypeLong: 173 | if mf.flags&flagNotNULL != 0 { 174 | if mf.flags&flagUnsigned != 0 { 175 | return scanTypeUint32 176 | } 177 | return scanTypeInt32 178 | } 179 | return scanTypeNullInt 180 | 181 | case fieldTypeLongLong: 182 | if mf.flags&flagNotNULL != 0 { 183 | if mf.flags&flagUnsigned != 0 { 184 | return scanTypeUint64 185 | } 186 | return scanTypeInt64 187 | } 188 | return scanTypeNullInt 189 | 190 | case fieldTypeFloat: 191 | if mf.flags&flagNotNULL != 0 { 192 | return scanTypeFloat32 193 | } 194 | return scanTypeNullFloat 195 | 196 | case fieldTypeDouble: 197 | if mf.flags&flagNotNULL != 0 { 198 | return scanTypeFloat64 199 | } 200 | return scanTypeNullFloat 201 | 202 | case fieldTypeBit, fieldTypeTinyBLOB, fieldTypeMediumBLOB, fieldTypeLongBLOB, 203 | fieldTypeBLOB, fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeVector: 204 | if mf.charSet == binaryCollationID { 205 | return scanTypeBytes 206 | } 207 | fallthrough 208 | case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, 209 | fieldTypeEnum, fieldTypeSet, fieldTypeJSON, fieldTypeTime: 210 | if mf.flags&flagNotNULL != 0 { 211 | return scanTypeString 212 | } 213 | return scanTypeNullString 214 | 215 | case fieldTypeDate, fieldTypeNewDate, 216 | fieldTypeTimestamp, fieldTypeDateTime: 217 | // NullTime is always returned for more consistent behavior as it can 218 | // handle both cases of parseTime regardless if the field is nullable. 219 | return scanTypeNullTime 220 | 221 | default: 222 | return scanTypeUnknown 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/infile.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "fmt" 13 | "io" 14 | "os" 15 | "strings" 16 | "sync" 17 | ) 18 | 19 | var ( 20 | fileRegister map[string]struct{} 21 | fileRegisterLock sync.RWMutex 22 | readerRegister map[string]func() io.Reader 23 | readerRegisterLock sync.RWMutex 24 | ) 25 | 26 | // RegisterLocalFile adds the given file to the file allowlist, 27 | // so that it can be used by "LOAD DATA LOCAL INFILE ". 28 | // Alternatively you can allow the use of all local files with 29 | // the DSN parameter 'allowAllFiles=true' 30 | // 31 | // filePath := "/home/gopher/data.csv" 32 | // mysql.RegisterLocalFile(filePath) 33 | // err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo") 34 | // if err != nil { 35 | // ... 36 | func RegisterLocalFile(filePath string) { 37 | fileRegisterLock.Lock() 38 | // lazy map init 39 | if fileRegister == nil { 40 | fileRegister = make(map[string]struct{}) 41 | } 42 | 43 | fileRegister[strings.Trim(filePath, `"`)] = struct{}{} 44 | fileRegisterLock.Unlock() 45 | } 46 | 47 | // DeregisterLocalFile removes the given filepath from the allowlist. 48 | func DeregisterLocalFile(filePath string) { 49 | fileRegisterLock.Lock() 50 | delete(fileRegister, strings.Trim(filePath, `"`)) 51 | fileRegisterLock.Unlock() 52 | } 53 | 54 | // RegisterReaderHandler registers a handler function which is used 55 | // to receive a io.Reader. 56 | // The Reader can be used by "LOAD DATA LOCAL INFILE Reader::". 57 | // If the handler returns a io.ReadCloser Close() is called when the 58 | // request is finished. 59 | // 60 | // mysql.RegisterReaderHandler("data", func() io.Reader { 61 | // var csvReader io.Reader // Some Reader that returns CSV data 62 | // ... // Open Reader here 63 | // return csvReader 64 | // }) 65 | // err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo") 66 | // if err != nil { 67 | // ... 68 | func RegisterReaderHandler(name string, handler func() io.Reader) { 69 | readerRegisterLock.Lock() 70 | // lazy map init 71 | if readerRegister == nil { 72 | readerRegister = make(map[string]func() io.Reader) 73 | } 74 | 75 | readerRegister[name] = handler 76 | readerRegisterLock.Unlock() 77 | } 78 | 79 | // DeregisterReaderHandler removes the ReaderHandler function with 80 | // the given name from the registry. 81 | func DeregisterReaderHandler(name string) { 82 | readerRegisterLock.Lock() 83 | delete(readerRegister, name) 84 | readerRegisterLock.Unlock() 85 | } 86 | 87 | func deferredClose(err *error, closer io.Closer) { 88 | closeErr := closer.Close() 89 | if *err == nil { 90 | *err = closeErr 91 | } 92 | } 93 | 94 | const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP 95 | 96 | func (mc *okHandler) handleInFileRequest(name string) (err error) { 97 | var rdr io.Reader 98 | packetSize := defaultPacketSize 99 | if mc.maxWriteSize < packetSize { 100 | packetSize = mc.maxWriteSize 101 | } 102 | 103 | if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader 104 | // The server might return an an absolute path. See issue #355. 105 | name = name[idx+8:] 106 | 107 | readerRegisterLock.RLock() 108 | handler, inMap := readerRegister[name] 109 | readerRegisterLock.RUnlock() 110 | 111 | if inMap { 112 | rdr = handler() 113 | if rdr != nil { 114 | if cl, ok := rdr.(io.Closer); ok { 115 | defer deferredClose(&err, cl) 116 | } 117 | } else { 118 | err = fmt.Errorf("reader '%s' is ", name) 119 | } 120 | } else { 121 | err = fmt.Errorf("reader '%s' is not registered", name) 122 | } 123 | } else { // File 124 | name = strings.Trim(name, `"`) 125 | fileRegisterLock.RLock() 126 | _, exists := fileRegister[name] 127 | fileRegisterLock.RUnlock() 128 | if mc.cfg.AllowAllFiles || exists { 129 | var file *os.File 130 | var fi os.FileInfo 131 | 132 | if file, err = os.Open(name); err == nil { 133 | defer deferredClose(&err, file) 134 | 135 | // get file size 136 | if fi, err = file.Stat(); err == nil { 137 | rdr = file 138 | if fileSize := int(fi.Size()); fileSize < packetSize { 139 | packetSize = fileSize 140 | } 141 | } 142 | } 143 | } else { 144 | err = fmt.Errorf("local file '%s' is not registered", name) 145 | } 146 | } 147 | 148 | // send content packets 149 | var data []byte 150 | 151 | // if packetSize == 0, the Reader contains no data 152 | if err == nil && packetSize > 0 { 153 | data = make([]byte, 4+packetSize) 154 | var n int 155 | for err == nil { 156 | n, err = rdr.Read(data[4:]) 157 | if n > 0 { 158 | if ioErr := mc.conn().writePacket(data[:4+n]); ioErr != nil { 159 | return ioErr 160 | } 161 | } 162 | } 163 | if err == io.EOF { 164 | err = nil 165 | } 166 | } 167 | 168 | // send empty packet (termination) 169 | if data == nil { 170 | data = make([]byte, 4) 171 | } 172 | if ioErr := mc.conn().writePacket(data[:4]); ioErr != nil { 173 | return ioErr 174 | } 175 | mc.conn().syncSequence() 176 | 177 | // read OK packet 178 | if err == nil { 179 | return mc.readResultOK() 180 | } 181 | 182 | mc.conn().readPacket() 183 | return err 184 | } 185 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/nulltime.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql" 13 | "database/sql/driver" 14 | "fmt" 15 | "time" 16 | ) 17 | 18 | // NullTime represents a time.Time that may be NULL. 19 | // NullTime implements the Scanner interface so 20 | // it can be used as a scan destination: 21 | // 22 | // var nt NullTime 23 | // err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt) 24 | // ... 25 | // if nt.Valid { 26 | // // use nt.Time 27 | // } else { 28 | // // NULL value 29 | // } 30 | // 31 | // # This NullTime implementation is not driver-specific 32 | // 33 | // Deprecated: NullTime doesn't honor the loc DSN parameter. 34 | // NullTime.Scan interprets a time as UTC, not the loc DSN parameter. 35 | // Use sql.NullTime instead. 36 | type NullTime sql.NullTime 37 | 38 | // Scan implements the Scanner interface. 39 | // The value type must be time.Time or string / []byte (formatted time-string), 40 | // otherwise Scan fails. 41 | func (nt *NullTime) Scan(value any) (err error) { 42 | if value == nil { 43 | nt.Time, nt.Valid = time.Time{}, false 44 | return 45 | } 46 | 47 | switch v := value.(type) { 48 | case time.Time: 49 | nt.Time, nt.Valid = v, true 50 | return 51 | case []byte: 52 | nt.Time, err = parseDateTime(v, time.UTC) 53 | nt.Valid = (err == nil) 54 | return 55 | case string: 56 | nt.Time, err = parseDateTime([]byte(v), time.UTC) 57 | nt.Valid = (err == nil) 58 | return 59 | } 60 | 61 | nt.Valid = false 62 | return fmt.Errorf("can't convert %T to time.Time", value) 63 | } 64 | 65 | // Value implements the driver Valuer interface. 66 | func (nt NullTime) Value() (driver.Value, error) { 67 | if !nt.Valid { 68 | return nil, nil 69 | } 70 | return nt.Time, nil 71 | } 72 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/result.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import "database/sql/driver" 12 | 13 | // Result exposes data not available through *connection.Result. 14 | // 15 | // This is accessible by executing statements using sql.Conn.Raw() and 16 | // downcasting the returned result: 17 | // 18 | // res, err := rawConn.Exec(...) 19 | // res.(mysql.Result).AllRowsAffected() 20 | type Result interface { 21 | driver.Result 22 | // AllRowsAffected returns a slice containing the affected rows for each 23 | // executed statement. 24 | AllRowsAffected() []int64 25 | // AllLastInsertIds returns a slice containing the last inserted ID for each 26 | // executed statement. 27 | AllLastInsertIds() []int64 28 | } 29 | 30 | type mysqlResult struct { 31 | // One entry in both slices is created for every executed statement result. 32 | affectedRows []int64 33 | insertIds []int64 34 | } 35 | 36 | func (res *mysqlResult) LastInsertId() (int64, error) { 37 | return res.insertIds[len(res.insertIds)-1], nil 38 | } 39 | 40 | func (res *mysqlResult) RowsAffected() (int64, error) { 41 | return res.affectedRows[len(res.affectedRows)-1], nil 42 | } 43 | 44 | func (res *mysqlResult) AllLastInsertIds() []int64 { 45 | return append([]int64{}, res.insertIds...) // defensive copy 46 | } 47 | 48 | func (res *mysqlResult) AllRowsAffected() []int64 { 49 | return append([]int64{}, res.affectedRows...) // defensive copy 50 | } 51 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/rows.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "io" 14 | "math" 15 | "reflect" 16 | ) 17 | 18 | type resultSet struct { 19 | columns []mysqlField 20 | columnNames []string 21 | done bool 22 | } 23 | 24 | type mysqlRows struct { 25 | mc *mysqlConn 26 | rs resultSet 27 | finish func() 28 | } 29 | 30 | type binaryRows struct { 31 | mysqlRows 32 | } 33 | 34 | type textRows struct { 35 | mysqlRows 36 | } 37 | 38 | func (rows *mysqlRows) Columns() []string { 39 | if rows.rs.columnNames != nil { 40 | return rows.rs.columnNames 41 | } 42 | 43 | columns := make([]string, len(rows.rs.columns)) 44 | if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias { 45 | for i := range columns { 46 | if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 { 47 | columns[i] = tableName + "." + rows.rs.columns[i].name 48 | } else { 49 | columns[i] = rows.rs.columns[i].name 50 | } 51 | } 52 | } else { 53 | for i := range columns { 54 | columns[i] = rows.rs.columns[i].name 55 | } 56 | } 57 | 58 | rows.rs.columnNames = columns 59 | return columns 60 | } 61 | 62 | func (rows *mysqlRows) ColumnTypeDatabaseTypeName(i int) string { 63 | return rows.rs.columns[i].typeDatabaseName() 64 | } 65 | 66 | // func (rows *mysqlRows) ColumnTypeLength(i int) (length int64, ok bool) { 67 | // return int64(rows.rs.columns[i].length), true 68 | // } 69 | 70 | func (rows *mysqlRows) ColumnTypeNullable(i int) (nullable, ok bool) { 71 | return rows.rs.columns[i].flags&flagNotNULL == 0, true 72 | } 73 | 74 | func (rows *mysqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) { 75 | column := rows.rs.columns[i] 76 | decimals := int64(column.decimals) 77 | 78 | switch column.fieldType { 79 | case fieldTypeDecimal, fieldTypeNewDecimal: 80 | if decimals > 0 { 81 | return int64(column.length) - 2, decimals, true 82 | } 83 | return int64(column.length) - 1, decimals, true 84 | case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeTime: 85 | return decimals, decimals, true 86 | case fieldTypeFloat, fieldTypeDouble: 87 | if decimals == 0x1f { 88 | return math.MaxInt64, math.MaxInt64, true 89 | } 90 | return math.MaxInt64, decimals, true 91 | } 92 | 93 | return 0, 0, false 94 | } 95 | 96 | func (rows *mysqlRows) ColumnTypeScanType(i int) reflect.Type { 97 | return rows.rs.columns[i].scanType() 98 | } 99 | 100 | func (rows *mysqlRows) Close() (err error) { 101 | if f := rows.finish; f != nil { 102 | f() 103 | rows.finish = nil 104 | } 105 | 106 | mc := rows.mc 107 | if mc == nil { 108 | return nil 109 | } 110 | if err := mc.error(); err != nil { 111 | return err 112 | } 113 | 114 | // Remove unread packets from stream 115 | if !rows.rs.done { 116 | err = mc.readUntilEOF() 117 | } 118 | if err == nil { 119 | handleOk := mc.clearResult() 120 | if err = handleOk.discardResults(); err != nil { 121 | return err 122 | } 123 | } 124 | 125 | rows.mc = nil 126 | return err 127 | } 128 | 129 | func (rows *mysqlRows) HasNextResultSet() (b bool) { 130 | if rows.mc == nil { 131 | return false 132 | } 133 | return rows.mc.status&statusMoreResultsExists != 0 134 | } 135 | 136 | func (rows *mysqlRows) nextResultSet() (int, error) { 137 | if rows.mc == nil { 138 | return 0, io.EOF 139 | } 140 | if err := rows.mc.error(); err != nil { 141 | return 0, err 142 | } 143 | 144 | // Remove unread packets from stream 145 | if !rows.rs.done { 146 | if err := rows.mc.readUntilEOF(); err != nil { 147 | return 0, err 148 | } 149 | rows.rs.done = true 150 | } 151 | 152 | if !rows.HasNextResultSet() { 153 | rows.mc = nil 154 | return 0, io.EOF 155 | } 156 | rows.rs = resultSet{} 157 | // rows.mc.affectedRows and rows.mc.insertIds accumulate on each call to 158 | // nextResultSet. 159 | resLen, err := rows.mc.resultUnchanged().readResultSetHeaderPacket() 160 | if err != nil { 161 | // Clean up about multi-results flag 162 | rows.rs.done = true 163 | rows.mc.status = rows.mc.status & (^statusMoreResultsExists) 164 | } 165 | return resLen, err 166 | } 167 | 168 | func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) { 169 | for { 170 | resLen, err := rows.nextResultSet() 171 | if err != nil { 172 | return 0, err 173 | } 174 | 175 | if resLen > 0 { 176 | return resLen, nil 177 | } 178 | 179 | rows.rs.done = true 180 | } 181 | } 182 | 183 | func (rows *binaryRows) NextResultSet() error { 184 | resLen, err := rows.nextNotEmptyResultSet() 185 | if err != nil { 186 | return err 187 | } 188 | 189 | rows.rs.columns, err = rows.mc.readColumns(resLen) 190 | return err 191 | } 192 | 193 | func (rows *binaryRows) Next(dest []driver.Value) error { 194 | if mc := rows.mc; mc != nil { 195 | if err := mc.error(); err != nil { 196 | return err 197 | } 198 | 199 | // Fetch next row from stream 200 | return rows.readRow(dest) 201 | } 202 | return io.EOF 203 | } 204 | 205 | func (rows *textRows) NextResultSet() (err error) { 206 | resLen, err := rows.nextNotEmptyResultSet() 207 | if err != nil { 208 | return err 209 | } 210 | 211 | rows.rs.columns, err = rows.mc.readColumns(resLen) 212 | return err 213 | } 214 | 215 | func (rows *textRows) Next(dest []driver.Value) error { 216 | if mc := rows.mc; mc != nil { 217 | if err := mc.error(); err != nil { 218 | return err 219 | } 220 | 221 | // Fetch next row from stream 222 | return rows.readRow(dest) 223 | } 224 | return io.EOF 225 | } 226 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/transaction.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlTx struct { 12 | mc *mysqlConn 13 | } 14 | 15 | func (tx *mysqlTx) Commit() (err error) { 16 | if tx.mc == nil { 17 | return ErrInvalidConn 18 | } 19 | if tx.mc.closed.Load() { 20 | err = tx.mc.error() 21 | if err == nil { 22 | err = ErrInvalidConn 23 | } 24 | return 25 | } 26 | err = tx.mc.exec("COMMIT") 27 | tx.mc = nil 28 | return 29 | } 30 | 31 | func (tx *mysqlTx) Rollback() (err error) { 32 | if tx.mc == nil { 33 | return ErrInvalidConn 34 | } 35 | if tx.mc.closed.Load() { 36 | err = tx.mc.error() 37 | if err == nil { 38 | err = ErrInvalidConn 39 | } 40 | return 41 | } 42 | err = tx.mc.exec("ROLLBACK") 43 | tx.mc = nil 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/v2/.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.swp 3 | *.8 4 | *.6 5 | _obj 6 | _test* 7 | markdown 8 | tags 9 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/v2/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - "1.10.x" 5 | - "1.11.x" 6 | - tip 7 | matrix: 8 | fast_finish: true 9 | allow_failures: 10 | - go: tip 11 | install: 12 | - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). 13 | script: 14 | - go get -t -v ./... 15 | - diff -u <(echo -n) <(gofmt -d -s .) 16 | - go tool vet . 17 | - go test -v ./... 18 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/v2/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Blackfriday is distributed under the Simplified BSD License: 2 | 3 | > Copyright © 2011 Russ Ross 4 | > All rights reserved. 5 | > 6 | > Redistribution and use in source and binary forms, with or without 7 | > modification, are permitted provided that the following conditions 8 | > are met: 9 | > 10 | > 1. Redistributions of source code must retain the above copyright 11 | > notice, this list of conditions and the following disclaimer. 12 | > 13 | > 2. Redistributions in binary form must reproduce the above 14 | > copyright notice, this list of conditions and the following 15 | > disclaimer in the documentation and/or other materials provided with 16 | > the distribution. 17 | > 18 | > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | > "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | > FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | > COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | > INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | > BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | > CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | > LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | > ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | > POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package blackfriday is a markdown processor. 2 | // 3 | // It translates plain text with simple formatting rules into an AST, which can 4 | // then be further processed to HTML (provided by Blackfriday itself) or other 5 | // formats (provided by the community). 6 | // 7 | // The simplest way to invoke Blackfriday is to call the Run function. It will 8 | // take a text input and produce a text output in HTML (or other format). 9 | // 10 | // A slightly more sophisticated way to use Blackfriday is to create a Markdown 11 | // processor and to call Parse, which returns a syntax tree for the input 12 | // document. You can leverage Blackfriday's parsing for content extraction from 13 | // markdown documents. You can assign a custom renderer and set various options 14 | // to the Markdown processor. 15 | // 16 | // If you're interested in calling Blackfriday from command line, see 17 | // https://github.com/russross/blackfriday-tool. 18 | // 19 | // Sanitized Anchor Names 20 | // 21 | // Blackfriday includes an algorithm for creating sanitized anchor names 22 | // corresponding to a given input text. This algorithm is used to create 23 | // anchors for headings when AutoHeadingIDs extension is enabled. The 24 | // algorithm is specified below, so that other packages can create 25 | // compatible anchor names and links to those anchors. 26 | // 27 | // The algorithm iterates over the input text, interpreted as UTF-8, 28 | // one Unicode code point (rune) at a time. All runes that are letters (category L) 29 | // or numbers (category N) are considered valid characters. They are mapped to 30 | // lower case, and included in the output. All other runes are considered 31 | // invalid characters. Invalid characters that precede the first valid character, 32 | // as well as invalid character that follow the last valid character 33 | // are dropped completely. All other sequences of invalid characters 34 | // between two valid characters are replaced with a single dash character '-'. 35 | // 36 | // SanitizedAnchorName exposes this functionality, and can be used to 37 | // create compatible links to the anchor names generated by blackfriday. 38 | // This algorithm is also implemented in a small standalone package at 39 | // github.com/shurcooL/sanitized_anchor_name. It can be useful for clients 40 | // that want a small package and don't need full functionality of blackfriday. 41 | package blackfriday 42 | 43 | // NOTE: Keep Sanitized Anchor Name algorithm in sync with package 44 | // github.com/shurcooL/sanitized_anchor_name. 45 | // Otherwise, users of sanitized_anchor_name will get anchor names 46 | // that are incompatible with those generated by blackfriday. 47 | -------------------------------------------------------------------------------- /vendor/github.com/russross/blackfriday/v2/esc.go: -------------------------------------------------------------------------------- 1 | package blackfriday 2 | 3 | import ( 4 | "html" 5 | "io" 6 | ) 7 | 8 | var htmlEscaper = [256][]byte{ 9 | '&': []byte("&"), 10 | '<': []byte("<"), 11 | '>': []byte(">"), 12 | '"': []byte("""), 13 | } 14 | 15 | func escapeHTML(w io.Writer, s []byte) { 16 | escapeEntities(w, s, false) 17 | } 18 | 19 | func escapeAllHTML(w io.Writer, s []byte) { 20 | escapeEntities(w, s, true) 21 | } 22 | 23 | func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) { 24 | var start, end int 25 | for end < len(s) { 26 | escSeq := htmlEscaper[s[end]] 27 | if escSeq != nil { 28 | isEntity, entityEnd := nodeIsEntity(s, end) 29 | if isEntity && !escapeValidEntities { 30 | w.Write(s[start : entityEnd+1]) 31 | start = entityEnd + 1 32 | } else { 33 | w.Write(s[start:end]) 34 | w.Write(escSeq) 35 | start = end + 1 36 | } 37 | } 38 | end++ 39 | } 40 | if start < len(s) && end <= len(s) { 41 | w.Write(s[start:end]) 42 | } 43 | } 44 | 45 | func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) { 46 | isEntity = false 47 | endEntityPos = end + 1 48 | 49 | if s[end] == '&' { 50 | for endEntityPos < len(s) { 51 | if s[endEntityPos] == ';' { 52 | if entities[string(s[end:endEntityPos+1])] { 53 | isEntity = true 54 | break 55 | } 56 | } 57 | if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' { 58 | break 59 | } 60 | endEntityPos++ 61 | } 62 | } 63 | 64 | return isEntity, endEntityPos 65 | } 66 | 67 | func escLink(w io.Writer, text []byte) { 68 | unesc := html.UnescapeString(string(text)) 69 | escapeHTML(w, []byte(unesc)) 70 | } 71 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. 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. 22 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_order.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // isOrdered checks that collection contains orderable elements. 9 | func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { 10 | objKind := reflect.TypeOf(object).Kind() 11 | if objKind != reflect.Slice && objKind != reflect.Array { 12 | return false 13 | } 14 | 15 | objValue := reflect.ValueOf(object) 16 | objLen := objValue.Len() 17 | 18 | if objLen <= 1 { 19 | return true 20 | } 21 | 22 | value := objValue.Index(0) 23 | valueInterface := value.Interface() 24 | firstValueKind := value.Kind() 25 | 26 | for i := 1; i < objLen; i++ { 27 | prevValue := value 28 | prevValueInterface := valueInterface 29 | 30 | value = objValue.Index(i) 31 | valueInterface = value.Interface() 32 | 33 | compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) 34 | 35 | if !isComparable { 36 | return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) 37 | } 38 | 39 | if !containsValue(allowedComparesResults, compareResult) { 40 | return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) 41 | } 42 | } 43 | 44 | return true 45 | } 46 | 47 | // IsIncreasing asserts that the collection is increasing 48 | // 49 | // assert.IsIncreasing(t, []int{1, 2, 3}) 50 | // assert.IsIncreasing(t, []float{1, 2}) 51 | // assert.IsIncreasing(t, []string{"a", "b"}) 52 | func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 53 | return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) 54 | } 55 | 56 | // IsNonIncreasing asserts that the collection is not increasing 57 | // 58 | // assert.IsNonIncreasing(t, []int{2, 1, 1}) 59 | // assert.IsNonIncreasing(t, []float{2, 1}) 60 | // assert.IsNonIncreasing(t, []string{"b", "a"}) 61 | func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 62 | return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) 63 | } 64 | 65 | // IsDecreasing asserts that the collection is decreasing 66 | // 67 | // assert.IsDecreasing(t, []int{2, 1, 0}) 68 | // assert.IsDecreasing(t, []float{2, 1}) 69 | // assert.IsDecreasing(t, []string{"b", "a"}) 70 | func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 71 | return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) 72 | } 73 | 74 | // IsNonDecreasing asserts that the collection is not decreasing 75 | // 76 | // assert.IsNonDecreasing(t, []int{1, 1, 2}) 77 | // assert.IsNonDecreasing(t, []float{1, 2}) 78 | // assert.IsNonDecreasing(t, []string{"a", "b"}) 79 | func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { 80 | return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // # Note 4 | // 5 | // All functions in this package return a bool value indicating whether the assertion has passed. 6 | // 7 | // # Example Usage 8 | // 9 | // The following is a complete example using assert in a standard test function: 10 | // 11 | // import ( 12 | // "testing" 13 | // "github.com/stretchr/testify/assert" 14 | // ) 15 | // 16 | // func TestSomething(t *testing.T) { 17 | // 18 | // var a string = "Hello" 19 | // var b string = "Hello" 20 | // 21 | // assert.Equal(t, a, b, "The two words should be the same.") 22 | // 23 | // } 24 | // 25 | // if you assert many times, use the format below: 26 | // 27 | // import ( 28 | // "testing" 29 | // "github.com/stretchr/testify/assert" 30 | // ) 31 | // 32 | // func TestSomething(t *testing.T) { 33 | // assert := assert.New(t) 34 | // 35 | // var a string = "Hello" 36 | // var b string = "Hello" 37 | // 38 | // assert.Equal(a, b, "The two words should be the same.") 39 | // } 40 | // 41 | // # Assertions 42 | // 43 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 44 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 45 | // testing framework. This allows the assertion funcs to write the failings and other details to 46 | // the correct place. 47 | // 48 | // Every assertion function also takes an optional string message as the final argument, 49 | // allowing custom error messages to be appended to the message the assertion method outputs. 50 | package assert 51 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default 2 | 3 | // Package yaml is an implementation of YAML functions that calls a pluggable implementation. 4 | // 5 | // This implementation is selected with the testify_yaml_custom build tag. 6 | // 7 | // go test -tags testify_yaml_custom 8 | // 9 | // This implementation can be used at build time to replace the default implementation 10 | // to avoid linking with [gopkg.in/yaml.v3]. 11 | // 12 | // In your test package: 13 | // 14 | // import assertYaml "github.com/stretchr/testify/assert/yaml" 15 | // 16 | // func init() { 17 | // assertYaml.Unmarshal = func (in []byte, out interface{}) error { 18 | // // ... 19 | // return nil 20 | // } 21 | // } 22 | package yaml 23 | 24 | var Unmarshal func(in []byte, out interface{}) error 25 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go: -------------------------------------------------------------------------------- 1 | //go:build !testify_yaml_fail && !testify_yaml_custom 2 | 3 | // Package yaml is just an indirection to handle YAML deserialization. 4 | // 5 | // This package is just an indirection that allows the builder to override the 6 | // indirection with an alternative implementation of this package that uses 7 | // another implementation of YAML deserialization. This allows to not either not 8 | // use YAML deserialization at all, or to use another implementation than 9 | // [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). 10 | // 11 | // Alternative implementations are selected using build tags: 12 | // 13 | // - testify_yaml_fail: [Unmarshal] always fails with an error 14 | // - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it 15 | // before calling any of [github.com/stretchr/testify/assert.YAMLEq] or 16 | // [github.com/stretchr/testify/assert.YAMLEqf]. 17 | // 18 | // Usage: 19 | // 20 | // go test -tags testify_yaml_fail 21 | // 22 | // You can check with "go list" which implementation is linked: 23 | // 24 | // go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 25 | // go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 26 | // go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml 27 | // 28 | // [PR #1120]: https://github.com/stretchr/testify/pull/1120 29 | package yaml 30 | 31 | import goyaml "gopkg.in/yaml.v3" 32 | 33 | // Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. 34 | func Unmarshal(in []byte, out interface{}) error { 35 | return goyaml.Unmarshal(in, out) 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go: -------------------------------------------------------------------------------- 1 | //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default 2 | 3 | // Package yaml is an implementation of YAML functions that always fail. 4 | // 5 | // This implementation can be used at build time to replace the default implementation 6 | // to avoid linking with [gopkg.in/yaml.v3]: 7 | // 8 | // go test -tags testify_yaml_fail 9 | package yaml 10 | 11 | import "errors" 12 | 13 | var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") 14 | 15 | func Unmarshal([]byte, interface{}) error { 16 | return errNotImplemented 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | coverage.txt 3 | node_modules/ 4 | vendor 5 | .idea 6 | /.local/ 7 | /internal/ 8 | /site/ 9 | package.json 10 | package-lock.json 11 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be 59 | reviewed and investigated and will result in a response that is deemed necessary 60 | and appropriate to the circumstances. The project team is obligated to maintain 61 | confidentiality with regard to the reporter of an incident. Further details of 62 | specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jeremy Saenz & Contributors 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. 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/README.md: -------------------------------------------------------------------------------- 1 | cli 2 | === 3 | 4 | [![Run Tests](https://github.com/urfave/cli/actions/workflows/cli.yml/badge.svg?branch=v1-maint)](https://github.com/urfave/cli/actions/workflows/cli.yml) 5 | [![Go Reference](https://pkg.go.dev/badge/github.com/urfave/cli/.svg)](https://pkg.go.dev/github.com/urfave/cli/) 6 | [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) 7 | [![codecov](https://codecov.io/gh/urfave/cli/branch/v1-maint/graph/badge.svg)](https://codecov.io/gh/urfave/cli) 8 | 9 | cli is a simple, fast, and fun package for building command line apps in Go. The 10 | goal is to enable developers to write fast and distributable command line 11 | applications in an expressive way. 12 | 13 | ## Usage Documentation 14 | 15 | Usage documentation for `v1` is available [at the docs 16 | site](https://cli.urfave.org/v1/getting-started/) or in-tree at 17 | [./docs/v1/manual.md](./docs/v1/manual.md) 18 | 19 | ## Installation 20 | 21 | Make sure you have a working Go environment. Go version 1.18+ is supported. 22 | 23 | ### Supported platforms 24 | 25 | cli is tested against multiple versions of Go on Linux, and against the latest released 26 | version of Go on OS X and Windows. For full details, see 27 | [./.github/workflows/cli.yml](./.github/workflows/cli.yml). 28 | 29 | ### Build tags 30 | 31 | You can use the following build tags: 32 | 33 | #### `urfave_cli_no_docs` 34 | 35 | When set, this removes `ToMarkdown` and `ToMan` methods, so your application 36 | won't be able to call those. This reduces the resulting binary size by about 37 | 300-400 KB (measured using Go 1.18.1 on Linux/amd64), due to less dependencies. 38 | 39 | ### Using `v1` releases 40 | 41 | ``` 42 | $ go get github.com/urfave/cli 43 | ``` 44 | 45 | ```go 46 | ... 47 | import ( 48 | "github.com/urfave/cli" 49 | ) 50 | ... 51 | ``` 52 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return lexicographicLess(c[i].Name, c[j].Name) 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // return nil 16 | // } 17 | // 18 | // app.Run(os.Args) 19 | // } 20 | package cli 21 | 22 | //go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go 23 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/docs.go: -------------------------------------------------------------------------------- 1 | //go:build !urfave_cli_no_docs 2 | // +build !urfave_cli_no_docs 3 | 4 | package cli 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "io" 10 | "sort" 11 | "strings" 12 | "text/template" 13 | 14 | "github.com/cpuguy83/go-md2man/v2/md2man" 15 | ) 16 | 17 | // ToMarkdown creates a markdown string for the `*App` 18 | // The function errors if either parsing or writing of the string fails. 19 | func (a *App) ToMarkdown() (string, error) { 20 | var w bytes.Buffer 21 | if err := a.writeDocTemplate(&w); err != nil { 22 | return "", err 23 | } 24 | return w.String(), nil 25 | } 26 | 27 | // ToMan creates a man page string for the `*App` 28 | // The function errors if either parsing or writing of the string fails. 29 | func (a *App) ToMan() (string, error) { 30 | var w bytes.Buffer 31 | if err := a.writeDocTemplate(&w); err != nil { 32 | return "", err 33 | } 34 | man := md2man.Render(w.Bytes()) 35 | return string(man), nil 36 | } 37 | 38 | type cliTemplate struct { 39 | App *App 40 | Commands []string 41 | GlobalArgs []string 42 | SynopsisArgs []string 43 | } 44 | 45 | func (a *App) writeDocTemplate(w io.Writer) error { 46 | const name = "cli" 47 | t, err := template.New(name).Parse(MarkdownDocTemplate) 48 | if err != nil { 49 | return err 50 | } 51 | return t.ExecuteTemplate(w, name, &cliTemplate{ 52 | App: a, 53 | Commands: prepareCommands(a.Commands, 0), 54 | GlobalArgs: prepareArgsWithValues(a.Flags), 55 | SynopsisArgs: prepareArgsSynopsis(a.Flags), 56 | }) 57 | } 58 | 59 | func prepareCommands(commands []Command, level int) []string { 60 | coms := []string{} 61 | for i := range commands { 62 | command := &commands[i] 63 | if command.Hidden { 64 | continue 65 | } 66 | usage := "" 67 | if command.Usage != "" { 68 | usage = command.Usage 69 | } 70 | 71 | prepared := fmt.Sprintf("%s %s\n\n%s\n", 72 | strings.Repeat("#", level+2), 73 | strings.Join(command.Names(), ", "), 74 | usage, 75 | ) 76 | 77 | flags := prepareArgsWithValues(command.Flags) 78 | if len(flags) > 0 { 79 | prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) 80 | } 81 | 82 | coms = append(coms, prepared) 83 | 84 | // recursevly iterate subcommands 85 | if len(command.Subcommands) > 0 { 86 | coms = append( 87 | coms, 88 | prepareCommands(command.Subcommands, level+1)..., 89 | ) 90 | } 91 | } 92 | 93 | return coms 94 | } 95 | 96 | func prepareArgsWithValues(flags []Flag) []string { 97 | return prepareFlags(flags, ", ", "**", "**", `""`, true) 98 | } 99 | 100 | func prepareArgsSynopsis(flags []Flag) []string { 101 | return prepareFlags(flags, "|", "[", "]", "[value]", false) 102 | } 103 | 104 | func prepareFlags( 105 | flags []Flag, 106 | sep, opener, closer, value string, 107 | addDetails bool, 108 | ) []string { 109 | args := []string{} 110 | for _, f := range flags { 111 | flag, ok := f.(DocGenerationFlag) 112 | if !ok { 113 | continue 114 | } 115 | modifiedArg := opener 116 | for _, s := range strings.Split(flag.GetName(), ",") { 117 | trimmed := strings.TrimSpace(s) 118 | if len(modifiedArg) > len(opener) { 119 | modifiedArg += sep 120 | } 121 | if len(trimmed) > 1 { 122 | modifiedArg += fmt.Sprintf("--%s", trimmed) 123 | } else { 124 | modifiedArg += fmt.Sprintf("-%s", trimmed) 125 | } 126 | } 127 | modifiedArg += closer 128 | if flag.TakesValue() { 129 | modifiedArg += fmt.Sprintf("=%s", value) 130 | } 131 | 132 | if addDetails { 133 | modifiedArg += flagDetails(flag) 134 | } 135 | 136 | args = append(args, modifiedArg+"\n") 137 | 138 | } 139 | sort.Strings(args) 140 | return args 141 | } 142 | 143 | // flagDetails returns a string containing the flags metadata 144 | func flagDetails(flag DocGenerationFlag) string { 145 | description := flag.GetUsage() 146 | value := flag.GetValue() 147 | if value != "" { 148 | description += " (default: " + value + ")" 149 | } 150 | return ": " + description 151 | } 152 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/fish.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "strings" 8 | "text/template" 9 | ) 10 | 11 | // ToFishCompletion creates a fish completion string for the `*App` 12 | // The function errors if either parsing or writing of the string fails. 13 | func (a *App) ToFishCompletion() (string, error) { 14 | var w bytes.Buffer 15 | if err := a.writeFishCompletionTemplate(&w); err != nil { 16 | return "", err 17 | } 18 | return w.String(), nil 19 | } 20 | 21 | type fishCompletionTemplate struct { 22 | App *App 23 | Completions []string 24 | AllCommands []string 25 | } 26 | 27 | func (a *App) writeFishCompletionTemplate(w io.Writer) error { 28 | const name = "cli" 29 | t, err := template.New(name).Parse(FishCompletionTemplate) 30 | if err != nil { 31 | return err 32 | } 33 | allCommands := []string{} 34 | 35 | // Add global flags 36 | completions := a.prepareFishFlags(a.VisibleFlags(), allCommands) 37 | 38 | // Add help flag 39 | if !a.HideHelp { 40 | completions = append( 41 | completions, 42 | a.prepareFishFlags([]Flag{HelpFlag}, allCommands)..., 43 | ) 44 | } 45 | 46 | // Add version flag 47 | if !a.HideVersion { 48 | completions = append( 49 | completions, 50 | a.prepareFishFlags([]Flag{VersionFlag}, allCommands)..., 51 | ) 52 | } 53 | 54 | // Add commands and their flags 55 | completions = append( 56 | completions, 57 | a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})..., 58 | ) 59 | 60 | return t.ExecuteTemplate(w, name, &fishCompletionTemplate{ 61 | App: a, 62 | Completions: completions, 63 | AllCommands: allCommands, 64 | }) 65 | } 66 | 67 | func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string { 68 | completions := []string{} 69 | for i := range commands { 70 | command := &commands[i] 71 | 72 | if command.Hidden { 73 | continue 74 | } 75 | 76 | var completion strings.Builder 77 | completion.WriteString(fmt.Sprintf( 78 | "complete -r -c %s -n '%s' -a '%s'", 79 | a.Name, 80 | a.fishSubcommandHelper(previousCommands), 81 | strings.Join(command.Names(), " "), 82 | )) 83 | 84 | if command.Usage != "" { 85 | completion.WriteString(fmt.Sprintf(" -d '%s'", 86 | escapeSingleQuotes(command.Usage))) 87 | } 88 | 89 | if !command.HideHelp { 90 | completions = append( 91 | completions, 92 | a.prepareFishFlags([]Flag{HelpFlag}, command.Names())..., 93 | ) 94 | } 95 | 96 | *allCommands = append(*allCommands, command.Names()...) 97 | completions = append(completions, completion.String()) 98 | completions = append( 99 | completions, 100 | a.prepareFishFlags(command.Flags, command.Names())..., 101 | ) 102 | 103 | // recursevly iterate subcommands 104 | if len(command.Subcommands) > 0 { 105 | completions = append( 106 | completions, 107 | a.prepareFishCommands( 108 | command.Subcommands, allCommands, command.Names(), 109 | )..., 110 | ) 111 | } 112 | } 113 | 114 | return completions 115 | } 116 | 117 | func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string { 118 | completions := []string{} 119 | for _, f := range flags { 120 | flag, ok := f.(DocGenerationFlag) 121 | if !ok { 122 | continue 123 | } 124 | 125 | completion := &strings.Builder{} 126 | completion.WriteString(fmt.Sprintf( 127 | "complete -c %s -n '%s'", 128 | a.Name, 129 | a.fishSubcommandHelper(previousCommands), 130 | )) 131 | 132 | fishAddFileFlag(f, completion) 133 | 134 | for idx, opt := range strings.Split(flag.GetName(), ",") { 135 | if idx == 0 { 136 | completion.WriteString(fmt.Sprintf( 137 | " -l %s", strings.TrimSpace(opt), 138 | )) 139 | } else { 140 | completion.WriteString(fmt.Sprintf( 141 | " -s %s", strings.TrimSpace(opt), 142 | )) 143 | 144 | } 145 | } 146 | 147 | if flag.TakesValue() { 148 | completion.WriteString(" -r") 149 | } 150 | 151 | if flag.GetUsage() != "" { 152 | completion.WriteString(fmt.Sprintf(" -d '%s'", 153 | escapeSingleQuotes(flag.GetUsage()))) 154 | } 155 | 156 | completions = append(completions, completion.String()) 157 | } 158 | 159 | return completions 160 | } 161 | 162 | func fishAddFileFlag(flag Flag, completion *strings.Builder) { 163 | switch f := flag.(type) { 164 | case GenericFlag: 165 | if f.TakesFile { 166 | return 167 | } 168 | case StringFlag: 169 | if f.TakesFile { 170 | return 171 | } 172 | case StringSliceFlag: 173 | if f.TakesFile { 174 | return 175 | } 176 | } 177 | completion.WriteString(" -f") 178 | } 179 | 180 | func (a *App) fishSubcommandHelper(allCommands []string) string { 181 | fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name) 182 | if len(allCommands) > 0 { 183 | fishHelper = fmt.Sprintf( 184 | "__fish_seen_subcommand_from %s", 185 | strings.Join(allCommands, " "), 186 | ) 187 | } 188 | return fishHelper 189 | 190 | } 191 | 192 | func escapeSingleQuotes(input string) string { 193 | return strings.Replace(input, `'`, `\'`, -1) 194 | } 195 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_bool.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // BoolFlag is a flag with type bool 10 | type BoolFlag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Destination *bool 18 | } 19 | 20 | // String returns a readable representation of this value 21 | // (for usage defaults) 22 | func (f BoolFlag) String() string { 23 | return FlagStringer(f) 24 | } 25 | 26 | // GetName returns the name of the flag 27 | func (f BoolFlag) GetName() string { 28 | return f.Name 29 | } 30 | 31 | // IsRequired returns whether or not the flag is required 32 | func (f BoolFlag) IsRequired() bool { 33 | return f.Required 34 | } 35 | 36 | // TakesValue returns true of the flag takes a value, otherwise false 37 | func (f BoolFlag) TakesValue() bool { 38 | return false 39 | } 40 | 41 | // GetUsage returns the usage string for the flag 42 | func (f BoolFlag) GetUsage() string { 43 | return f.Usage 44 | } 45 | 46 | // GetValue returns the flags value as string representation and an empty 47 | // string if the flag takes no value at all. 48 | func (f BoolFlag) GetValue() string { 49 | return "" 50 | } 51 | 52 | // Bool looks up the value of a local BoolFlag, returns 53 | // false if not found 54 | func (c *Context) Bool(name string) bool { 55 | return lookupBool(name, c.flagSet) 56 | } 57 | 58 | // GlobalBool looks up the value of a global BoolFlag, returns 59 | // false if not found 60 | func (c *Context) GlobalBool(name string) bool { 61 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 62 | return lookupBool(name, fs) 63 | } 64 | return false 65 | } 66 | 67 | // Apply populates the flag given the flag set and environment 68 | // Ignores errors 69 | func (f BoolFlag) Apply(set *flag.FlagSet) { 70 | _ = f.ApplyWithError(set) 71 | } 72 | 73 | // ApplyWithError populates the flag given the flag set and environment 74 | func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { 75 | val := false 76 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 77 | if envVal == "" { 78 | val = false 79 | } else { 80 | envValBool, err := strconv.ParseBool(envVal) 81 | if err != nil { 82 | return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) 83 | } 84 | val = envValBool 85 | } 86 | } 87 | 88 | eachName(f.Name, func(name string) { 89 | if f.Destination != nil { 90 | set.BoolVar(f.Destination, name, val, f.Usage) 91 | return 92 | } 93 | set.Bool(name, val, f.Usage) 94 | }) 95 | 96 | return nil 97 | } 98 | 99 | func lookupBool(name string, set *flag.FlagSet) bool { 100 | f := set.Lookup(name) 101 | if f != nil { 102 | parsed, err := strconv.ParseBool(f.Value.String()) 103 | if err != nil { 104 | return false 105 | } 106 | return parsed 107 | } 108 | return false 109 | } 110 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_bool_t.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // BoolTFlag is a flag with type bool that is true by default 10 | type BoolTFlag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Destination *bool 18 | } 19 | 20 | // String returns a readable representation of this value 21 | // (for usage defaults) 22 | func (f BoolTFlag) String() string { 23 | return FlagStringer(f) 24 | } 25 | 26 | // GetName returns the name of the flag 27 | func (f BoolTFlag) GetName() string { 28 | return f.Name 29 | } 30 | 31 | // IsRequired returns whether or not the flag is required 32 | func (f BoolTFlag) IsRequired() bool { 33 | return f.Required 34 | } 35 | 36 | // TakesValue returns true of the flag takes a value, otherwise false 37 | func (f BoolTFlag) TakesValue() bool { 38 | return false 39 | } 40 | 41 | // GetUsage returns the usage string for the flag 42 | func (f BoolTFlag) GetUsage() string { 43 | return f.Usage 44 | } 45 | 46 | // GetValue returns the flags value as string representation and an empty 47 | // string if the flag takes no value at all. 48 | func (f BoolTFlag) GetValue() string { 49 | return "" 50 | } 51 | 52 | // BoolT looks up the value of a local BoolTFlag, returns 53 | // false if not found 54 | func (c *Context) BoolT(name string) bool { 55 | return lookupBoolT(name, c.flagSet) 56 | } 57 | 58 | // GlobalBoolT looks up the value of a global BoolTFlag, returns 59 | // false if not found 60 | func (c *Context) GlobalBoolT(name string) bool { 61 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 62 | return lookupBoolT(name, fs) 63 | } 64 | return false 65 | } 66 | 67 | // Apply populates the flag given the flag set and environment 68 | // Ignores errors 69 | func (f BoolTFlag) Apply(set *flag.FlagSet) { 70 | _ = f.ApplyWithError(set) 71 | } 72 | 73 | // ApplyWithError populates the flag given the flag set and environment 74 | func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { 75 | val := true 76 | 77 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 78 | if envVal == "" { 79 | val = false 80 | } else { 81 | envValBool, err := strconv.ParseBool(envVal) 82 | if err != nil { 83 | return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) 84 | } 85 | val = envValBool 86 | } 87 | } 88 | 89 | eachName(f.Name, func(name string) { 90 | if f.Destination != nil { 91 | set.BoolVar(f.Destination, name, val, f.Usage) 92 | return 93 | } 94 | set.Bool(name, val, f.Usage) 95 | }) 96 | 97 | return nil 98 | } 99 | 100 | func lookupBoolT(name string, set *flag.FlagSet) bool { 101 | f := set.Lookup(name) 102 | if f != nil { 103 | parsed, err := strconv.ParseBool(f.Value.String()) 104 | if err != nil { 105 | return false 106 | } 107 | return parsed 108 | } 109 | return false 110 | } 111 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_duration.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) 10 | type DurationFlag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value time.Duration 18 | Destination *time.Duration 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f DurationFlag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f DurationFlag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f DurationFlag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f DurationFlag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f DurationFlag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // GetValue returns the flags value as string representation and an empty 48 | // string if the flag takes no value at all. 49 | func (f DurationFlag) GetValue() string { 50 | return f.Value.String() 51 | } 52 | 53 | // Duration looks up the value of a local DurationFlag, returns 54 | // 0 if not found 55 | func (c *Context) Duration(name string) time.Duration { 56 | return lookupDuration(name, c.flagSet) 57 | } 58 | 59 | // GlobalDuration looks up the value of a global DurationFlag, returns 60 | // 0 if not found 61 | func (c *Context) GlobalDuration(name string) time.Duration { 62 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 63 | return lookupDuration(name, fs) 64 | } 65 | return 0 66 | } 67 | 68 | // Apply populates the flag given the flag set and environment 69 | // Ignores errors 70 | func (f DurationFlag) Apply(set *flag.FlagSet) { 71 | _ = f.ApplyWithError(set) 72 | } 73 | 74 | // ApplyWithError populates the flag given the flag set and environment 75 | func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { 76 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 77 | envValDuration, err := time.ParseDuration(envVal) 78 | if err != nil { 79 | return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) 80 | } 81 | 82 | f.Value = envValDuration 83 | } 84 | 85 | eachName(f.Name, func(name string) { 86 | if f.Destination != nil { 87 | set.DurationVar(f.Destination, name, f.Value, f.Usage) 88 | return 89 | } 90 | set.Duration(name, f.Value, f.Usage) 91 | }) 92 | 93 | return nil 94 | } 95 | 96 | func lookupDuration(name string, set *flag.FlagSet) time.Duration { 97 | f := set.Lookup(name) 98 | if f != nil { 99 | parsed, err := time.ParseDuration(f.Value.String()) 100 | if err != nil { 101 | return 0 102 | } 103 | return parsed 104 | } 105 | return 0 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_float64.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Float64Flag is a flag with type float64 10 | type Float64Flag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value float64 18 | Destination *float64 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f Float64Flag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f Float64Flag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f Float64Flag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f Float64Flag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f Float64Flag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // GetValue returns the flags value as string representation and an empty 48 | // string if the flag takes no value at all. 49 | func (f Float64Flag) GetValue() string { 50 | return fmt.Sprintf("%f", f.Value) 51 | } 52 | 53 | // Float64 looks up the value of a local Float64Flag, returns 54 | // 0 if not found 55 | func (c *Context) Float64(name string) float64 { 56 | return lookupFloat64(name, c.flagSet) 57 | } 58 | 59 | // GlobalFloat64 looks up the value of a global Float64Flag, returns 60 | // 0 if not found 61 | func (c *Context) GlobalFloat64(name string) float64 { 62 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 63 | return lookupFloat64(name, fs) 64 | } 65 | return 0 66 | } 67 | 68 | // Apply populates the flag given the flag set and environment 69 | // Ignores errors 70 | func (f Float64Flag) Apply(set *flag.FlagSet) { 71 | _ = f.ApplyWithError(set) 72 | } 73 | 74 | // ApplyWithError populates the flag given the flag set and environment 75 | func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { 76 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 77 | envValFloat, err := strconv.ParseFloat(envVal, 10) 78 | if err != nil { 79 | return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) 80 | } 81 | 82 | f.Value = envValFloat 83 | } 84 | 85 | eachName(f.Name, func(name string) { 86 | if f.Destination != nil { 87 | set.Float64Var(f.Destination, name, f.Value, f.Usage) 88 | return 89 | } 90 | set.Float64(name, f.Value, f.Usage) 91 | }) 92 | 93 | return nil 94 | } 95 | 96 | func lookupFloat64(name string, set *flag.FlagSet) float64 { 97 | f := set.Lookup(name) 98 | if f != nil { 99 | parsed, err := strconv.ParseFloat(f.Value.String(), 64) 100 | if err != nil { 101 | return 0 102 | } 103 | return parsed 104 | } 105 | return 0 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_generic.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | // Generic is a generic parseable type identified by a specific flag 9 | type Generic interface { 10 | Set(value string) error 11 | String() string 12 | } 13 | 14 | // GenericFlag is a flag with type Generic 15 | type GenericFlag struct { 16 | Name string 17 | Usage string 18 | EnvVar string 19 | FilePath string 20 | Required bool 21 | Hidden bool 22 | TakesFile bool 23 | Value Generic 24 | } 25 | 26 | // String returns a readable representation of this value 27 | // (for usage defaults) 28 | func (f GenericFlag) String() string { 29 | return FlagStringer(f) 30 | } 31 | 32 | // GetName returns the name of the flag 33 | func (f GenericFlag) GetName() string { 34 | return f.Name 35 | } 36 | 37 | // IsRequired returns whether or not the flag is required 38 | func (f GenericFlag) IsRequired() bool { 39 | return f.Required 40 | } 41 | 42 | // TakesValue returns true of the flag takes a value, otherwise false 43 | func (f GenericFlag) TakesValue() bool { 44 | return true 45 | } 46 | 47 | // GetUsage returns the usage string for the flag 48 | func (f GenericFlag) GetUsage() string { 49 | return f.Usage 50 | } 51 | 52 | // GetValue returns the flags value as string representation and an empty 53 | // string if the flag takes no value at all. 54 | func (f GenericFlag) GetValue() string { 55 | if f.Value != nil { 56 | return f.Value.String() 57 | } 58 | return "" 59 | } 60 | 61 | // Apply takes the flagset and calls Set on the generic flag with the value 62 | // provided by the user for parsing by the flag 63 | // Ignores parsing errors 64 | func (f GenericFlag) Apply(set *flag.FlagSet) { 65 | _ = f.ApplyWithError(set) 66 | } 67 | 68 | // ApplyWithError takes the flagset and calls Set on the generic flag with the value 69 | // provided by the user for parsing by the flag 70 | func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { 71 | val := f.Value 72 | if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 73 | if err := val.Set(fileEnvVal); err != nil { 74 | return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) 75 | } 76 | } 77 | 78 | eachName(f.Name, func(name string) { 79 | set.Var(f.Value, name, f.Usage) 80 | }) 81 | 82 | return nil 83 | } 84 | 85 | // Generic looks up the value of a local GenericFlag, returns 86 | // nil if not found 87 | func (c *Context) Generic(name string) interface{} { 88 | return lookupGeneric(name, c.flagSet) 89 | } 90 | 91 | // GlobalGeneric looks up the value of a global GenericFlag, returns 92 | // nil if not found 93 | func (c *Context) GlobalGeneric(name string) interface{} { 94 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 95 | return lookupGeneric(name, fs) 96 | } 97 | return nil 98 | } 99 | 100 | func lookupGeneric(name string, set *flag.FlagSet) interface{} { 101 | f := set.Lookup(name) 102 | if f != nil { 103 | parsed, err := f.Value, error(nil) 104 | if err != nil { 105 | return nil 106 | } 107 | return parsed 108 | } 109 | return nil 110 | } 111 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_int.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // IntFlag is a flag with type int 10 | type IntFlag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value int 18 | Destination *int 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f IntFlag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f IntFlag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f IntFlag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f IntFlag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f IntFlag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // GetValue returns the flags value as string representation and an empty 48 | // string if the flag takes no value at all. 49 | func (f IntFlag) GetValue() string { 50 | return fmt.Sprintf("%d", f.Value) 51 | } 52 | 53 | // Apply populates the flag given the flag set and environment 54 | // Ignores errors 55 | func (f IntFlag) Apply(set *flag.FlagSet) { 56 | _ = f.ApplyWithError(set) 57 | } 58 | 59 | // ApplyWithError populates the flag given the flag set and environment 60 | func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { 61 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 62 | envValInt, err := strconv.ParseInt(envVal, 0, 64) 63 | if err != nil { 64 | return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) 65 | } 66 | f.Value = int(envValInt) 67 | } 68 | 69 | eachName(f.Name, func(name string) { 70 | if f.Destination != nil { 71 | set.IntVar(f.Destination, name, f.Value, f.Usage) 72 | return 73 | } 74 | set.Int(name, f.Value, f.Usage) 75 | }) 76 | 77 | return nil 78 | } 79 | 80 | // Int looks up the value of a local IntFlag, returns 81 | // 0 if not found 82 | func (c *Context) Int(name string) int { 83 | return lookupInt(name, c.flagSet) 84 | } 85 | 86 | // GlobalInt looks up the value of a global IntFlag, returns 87 | // 0 if not found 88 | func (c *Context) GlobalInt(name string) int { 89 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 90 | return lookupInt(name, fs) 91 | } 92 | return 0 93 | } 94 | 95 | func lookupInt(name string, set *flag.FlagSet) int { 96 | f := set.Lookup(name) 97 | if f != nil { 98 | parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) 99 | if err != nil { 100 | return 0 101 | } 102 | return int(parsed) 103 | } 104 | return 0 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_int64.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Int64Flag is a flag with type int64 10 | type Int64Flag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value int64 18 | Destination *int64 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f Int64Flag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f Int64Flag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f Int64Flag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f Int64Flag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f Int64Flag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // GetValue returns the flags value as string representation and an empty 48 | // string if the flag takes no value at all. 49 | func (f Int64Flag) GetValue() string { 50 | return fmt.Sprintf("%d", f.Value) 51 | } 52 | 53 | // Apply populates the flag given the flag set and environment 54 | // Ignores errors 55 | func (f Int64Flag) Apply(set *flag.FlagSet) { 56 | _ = f.ApplyWithError(set) 57 | } 58 | 59 | // ApplyWithError populates the flag given the flag set and environment 60 | func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { 61 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 62 | envValInt, err := strconv.ParseInt(envVal, 0, 64) 63 | if err != nil { 64 | return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) 65 | } 66 | 67 | f.Value = envValInt 68 | } 69 | 70 | eachName(f.Name, func(name string) { 71 | if f.Destination != nil { 72 | set.Int64Var(f.Destination, name, f.Value, f.Usage) 73 | return 74 | } 75 | set.Int64(name, f.Value, f.Usage) 76 | }) 77 | 78 | return nil 79 | } 80 | 81 | // Int64 looks up the value of a local Int64Flag, returns 82 | // 0 if not found 83 | func (c *Context) Int64(name string) int64 { 84 | return lookupInt64(name, c.flagSet) 85 | } 86 | 87 | // GlobalInt64 looks up the value of a global Int64Flag, returns 88 | // 0 if not found 89 | func (c *Context) GlobalInt64(name string) int64 { 90 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 91 | return lookupInt64(name, fs) 92 | } 93 | return 0 94 | } 95 | 96 | func lookupInt64(name string, set *flag.FlagSet) int64 { 97 | f := set.Lookup(name) 98 | if f != nil { 99 | parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) 100 | if err != nil { 101 | return 0 102 | } 103 | return parsed 104 | } 105 | return 0 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_int64_slice.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter 11 | type Int64Slice []int64 12 | 13 | // Set parses the value into an integer and appends it to the list of values 14 | func (f *Int64Slice) Set(value string) error { 15 | tmp, err := strconv.ParseInt(value, 10, 64) 16 | if err != nil { 17 | return err 18 | } 19 | *f = append(*f, tmp) 20 | return nil 21 | } 22 | 23 | // String returns a readable representation of this value (for usage defaults) 24 | func (f *Int64Slice) String() string { 25 | slice := make([]string, len(*f)) 26 | for i, v := range *f { 27 | slice[i] = strconv.FormatInt(v, 10) 28 | } 29 | 30 | return strings.Join(slice, ",") 31 | } 32 | 33 | // Value returns the slice of ints set by this flag 34 | func (f *Int64Slice) Value() []int64 { 35 | return *f 36 | } 37 | 38 | // Get returns the slice of ints set by this flag 39 | func (f *Int64Slice) Get() interface{} { 40 | return *f 41 | } 42 | 43 | // Int64SliceFlag is a flag with type *Int64Slice 44 | type Int64SliceFlag struct { 45 | Name string 46 | Usage string 47 | EnvVar string 48 | FilePath string 49 | Required bool 50 | Hidden bool 51 | Value *Int64Slice 52 | } 53 | 54 | // String returns a readable representation of this value 55 | // (for usage defaults) 56 | func (f Int64SliceFlag) String() string { 57 | return FlagStringer(f) 58 | } 59 | 60 | // GetName returns the name of the flag 61 | func (f Int64SliceFlag) GetName() string { 62 | return f.Name 63 | } 64 | 65 | // IsRequired returns whether or not the flag is required 66 | func (f Int64SliceFlag) IsRequired() bool { 67 | return f.Required 68 | } 69 | 70 | // TakesValue returns true of the flag takes a value, otherwise false 71 | func (f Int64SliceFlag) TakesValue() bool { 72 | return true 73 | } 74 | 75 | // GetUsage returns the usage string for the flag 76 | func (f Int64SliceFlag) GetUsage() string { 77 | return f.Usage 78 | } 79 | 80 | // GetValue returns the flags value as string representation and an empty 81 | // string if the flag takes no value at all. 82 | func (f Int64SliceFlag) GetValue() string { 83 | if f.Value != nil { 84 | return f.Value.String() 85 | } 86 | return "" 87 | } 88 | 89 | // Apply populates the flag given the flag set and environment 90 | // Ignores errors 91 | func (f Int64SliceFlag) Apply(set *flag.FlagSet) { 92 | _ = f.ApplyWithError(set) 93 | } 94 | 95 | // ApplyWithError populates the flag given the flag set and environment 96 | func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { 97 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 98 | newVal := &Int64Slice{} 99 | for _, s := range strings.Split(envVal, ",") { 100 | s = strings.TrimSpace(s) 101 | if err := newVal.Set(s); err != nil { 102 | return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) 103 | } 104 | } 105 | if f.Value == nil { 106 | f.Value = newVal 107 | } else { 108 | *f.Value = *newVal 109 | } 110 | } 111 | 112 | eachName(f.Name, func(name string) { 113 | if f.Value == nil { 114 | f.Value = &Int64Slice{} 115 | } 116 | set.Var(f.Value, name, f.Usage) 117 | }) 118 | 119 | return nil 120 | } 121 | 122 | // Int64Slice looks up the value of a local Int64SliceFlag, returns 123 | // nil if not found 124 | func (c *Context) Int64Slice(name string) []int64 { 125 | return lookupInt64Slice(name, c.flagSet) 126 | } 127 | 128 | // GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns 129 | // nil if not found 130 | func (c *Context) GlobalInt64Slice(name string) []int64 { 131 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 132 | return lookupInt64Slice(name, fs) 133 | } 134 | return nil 135 | } 136 | 137 | func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { 138 | f := set.Lookup(name) 139 | if f != nil { 140 | value, ok := f.Value.(*Int64Slice) 141 | if !ok { 142 | return nil 143 | } 144 | 145 | // extract the slice from asserted value 146 | parsed := value.Value() 147 | 148 | // extract default value from the flag 149 | var defaultVal []int64 150 | for _, v := range strings.Split(f.DefValue, ",") { 151 | if v != "" { 152 | int64Value, err := strconv.ParseInt(v, 10, 64) 153 | if err != nil { 154 | panic(err) 155 | } 156 | defaultVal = append(defaultVal, int64Value) 157 | } 158 | } 159 | // if the current value is not equal to the default value 160 | // remove the default values from the flag 161 | if !isInt64SliceEqual(parsed, defaultVal) { 162 | for _, v := range defaultVal { 163 | parsed = removeFromInt64Slice(parsed, v) 164 | } 165 | } 166 | return parsed 167 | } 168 | return nil 169 | } 170 | 171 | func removeFromInt64Slice(slice []int64, val int64) []int64 { 172 | for i, v := range slice { 173 | if v == val { 174 | ret := append([]int64{}, slice[:i]...) 175 | ret = append(ret, slice[i+1:]...) 176 | return ret 177 | } 178 | } 179 | return slice 180 | } 181 | 182 | func isInt64SliceEqual(newValue, defaultValue []int64) bool { 183 | // If one is nil, the other must also be nil. 184 | if (newValue == nil) != (defaultValue == nil) { 185 | return false 186 | } 187 | 188 | if len(newValue) != len(defaultValue) { 189 | return false 190 | } 191 | 192 | for i, v := range newValue { 193 | if v != defaultValue[i] { 194 | return false 195 | } 196 | } 197 | 198 | return true 199 | } 200 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_int_slice.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter 11 | type IntSlice []int 12 | 13 | // Set parses the value into an integer and appends it to the list of values 14 | func (f *IntSlice) Set(value string) error { 15 | tmp, err := strconv.Atoi(value) 16 | if err != nil { 17 | return err 18 | } 19 | *f = append(*f, tmp) 20 | return nil 21 | } 22 | 23 | // String returns a readable representation of this value (for usage defaults) 24 | func (f *IntSlice) String() string { 25 | slice := make([]string, len(*f)) 26 | for i, v := range *f { 27 | slice[i] = strconv.Itoa(v) 28 | } 29 | 30 | return strings.Join(slice, ",") 31 | } 32 | 33 | // Value returns the slice of ints set by this flag 34 | func (f *IntSlice) Value() []int { 35 | return *f 36 | } 37 | 38 | // Get returns the slice of ints set by this flag 39 | func (f *IntSlice) Get() interface{} { 40 | return *f 41 | } 42 | 43 | // IntSliceFlag is a flag with type *IntSlice 44 | type IntSliceFlag struct { 45 | Name string 46 | Usage string 47 | EnvVar string 48 | FilePath string 49 | Required bool 50 | Hidden bool 51 | Value *IntSlice 52 | } 53 | 54 | // String returns a readable representation of this value 55 | // (for usage defaults) 56 | func (f IntSliceFlag) String() string { 57 | return FlagStringer(f) 58 | } 59 | 60 | // GetName returns the name of the flag 61 | func (f IntSliceFlag) GetName() string { 62 | return f.Name 63 | } 64 | 65 | // IsRequired returns whether or not the flag is required 66 | func (f IntSliceFlag) IsRequired() bool { 67 | return f.Required 68 | } 69 | 70 | // TakesValue returns true of the flag takes a value, otherwise false 71 | func (f IntSliceFlag) TakesValue() bool { 72 | return true 73 | } 74 | 75 | // GetUsage returns the usage string for the flag 76 | func (f IntSliceFlag) GetUsage() string { 77 | return f.Usage 78 | } 79 | 80 | // GetValue returns the flags value as string representation and an empty 81 | // string if the flag takes no value at all. 82 | func (f IntSliceFlag) GetValue() string { 83 | if f.Value != nil { 84 | return f.Value.String() 85 | } 86 | return "" 87 | } 88 | 89 | // Apply populates the flag given the flag set and environment 90 | // Ignores errors 91 | func (f IntSliceFlag) Apply(set *flag.FlagSet) { 92 | _ = f.ApplyWithError(set) 93 | } 94 | 95 | // ApplyWithError populates the flag given the flag set and environment 96 | func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { 97 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 98 | newVal := &IntSlice{} 99 | for _, s := range strings.Split(envVal, ",") { 100 | s = strings.TrimSpace(s) 101 | if err := newVal.Set(s); err != nil { 102 | return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) 103 | } 104 | } 105 | if f.Value == nil { 106 | f.Value = newVal 107 | } else { 108 | *f.Value = *newVal 109 | } 110 | } 111 | 112 | eachName(f.Name, func(name string) { 113 | if f.Value == nil { 114 | f.Value = &IntSlice{} 115 | } 116 | set.Var(f.Value, name, f.Usage) 117 | }) 118 | 119 | return nil 120 | } 121 | 122 | // IntSlice looks up the value of a local IntSliceFlag, returns 123 | // nil if not found 124 | func (c *Context) IntSlice(name string) []int { 125 | return lookupIntSlice(name, c.flagSet) 126 | } 127 | 128 | // GlobalIntSlice looks up the value of a global IntSliceFlag, returns 129 | // nil if not found 130 | func (c *Context) GlobalIntSlice(name string) []int { 131 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 132 | return lookupIntSlice(name, fs) 133 | } 134 | return nil 135 | } 136 | 137 | func lookupIntSlice(name string, set *flag.FlagSet) []int { 138 | f := set.Lookup(name) 139 | if f != nil { 140 | value, ok := f.Value.(*IntSlice) 141 | if !ok { 142 | return nil 143 | } 144 | // extract the slice from asserted value 145 | slice := value.Value() 146 | 147 | // extract default value from the flag 148 | var defaultVal []int 149 | for _, v := range strings.Split(f.DefValue, ",") { 150 | if v != "" { 151 | intValue, err := strconv.Atoi(v) 152 | if err != nil { 153 | panic(err) 154 | } 155 | defaultVal = append(defaultVal, intValue) 156 | } 157 | } 158 | // if the current value is not equal to the default value 159 | // remove the default values from the flag 160 | if !isIntSliceEqual(slice, defaultVal) { 161 | for _, v := range defaultVal { 162 | slice = removeFromIntSlice(slice, v) 163 | } 164 | } 165 | return slice 166 | } 167 | return nil 168 | } 169 | 170 | func removeFromIntSlice(slice []int, val int) []int { 171 | for i, v := range slice { 172 | if v == val { 173 | ret := append([]int{}, slice[:i]...) 174 | ret = append(ret, slice[i+1:]...) 175 | return ret 176 | } 177 | } 178 | return slice 179 | } 180 | 181 | func isIntSliceEqual(newValue, defaultValue []int) bool { 182 | // If one is nil, the other must also be nil. 183 | if (newValue == nil) != (defaultValue == nil) { 184 | return false 185 | } 186 | 187 | if len(newValue) != len(defaultValue) { 188 | return false 189 | } 190 | 191 | for i, v := range newValue { 192 | if v != defaultValue[i] { 193 | return false 194 | } 195 | } 196 | 197 | return true 198 | } 199 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_string.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import "flag" 4 | 5 | // StringFlag is a flag with type string 6 | type StringFlag struct { 7 | Name string 8 | Usage string 9 | EnvVar string 10 | FilePath string 11 | Required bool 12 | Hidden bool 13 | TakesFile bool 14 | Value string 15 | Destination *string 16 | } 17 | 18 | // String returns a readable representation of this value 19 | // (for usage defaults) 20 | func (f StringFlag) String() string { 21 | return FlagStringer(f) 22 | } 23 | 24 | // GetName returns the name of the flag 25 | func (f StringFlag) GetName() string { 26 | return f.Name 27 | } 28 | 29 | // IsRequired returns whether or not the flag is required 30 | func (f StringFlag) IsRequired() bool { 31 | return f.Required 32 | } 33 | 34 | // TakesValue returns true of the flag takes a value, otherwise false 35 | func (f StringFlag) TakesValue() bool { 36 | return true 37 | } 38 | 39 | // GetUsage returns the usage string for the flag 40 | func (f StringFlag) GetUsage() string { 41 | return f.Usage 42 | } 43 | 44 | // GetValue returns the flags value as string representation and an empty 45 | // string if the flag takes no value at all. 46 | func (f StringFlag) GetValue() string { 47 | return f.Value 48 | } 49 | 50 | // Apply populates the flag given the flag set and environment 51 | // Ignores errors 52 | func (f StringFlag) Apply(set *flag.FlagSet) { 53 | _ = f.ApplyWithError(set) 54 | } 55 | 56 | // ApplyWithError populates the flag given the flag set and environment 57 | func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { 58 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 59 | f.Value = envVal 60 | } 61 | 62 | eachName(f.Name, func(name string) { 63 | if f.Destination != nil { 64 | set.StringVar(f.Destination, name, f.Value, f.Usage) 65 | return 66 | } 67 | set.String(name, f.Value, f.Usage) 68 | }) 69 | 70 | return nil 71 | } 72 | 73 | // String looks up the value of a local StringFlag, returns 74 | // "" if not found 75 | func (c *Context) String(name string) string { 76 | return lookupString(name, c.flagSet) 77 | } 78 | 79 | // GlobalString looks up the value of a global StringFlag, returns 80 | // "" if not found 81 | func (c *Context) GlobalString(name string) string { 82 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 83 | return lookupString(name, fs) 84 | } 85 | return "" 86 | } 87 | 88 | func lookupString(name string, set *flag.FlagSet) string { 89 | f := set.Lookup(name) 90 | if f != nil { 91 | parsed, err := f.Value.String(), error(nil) 92 | if err != nil { 93 | return "" 94 | } 95 | return parsed 96 | } 97 | return "" 98 | } 99 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_string_slice.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter 10 | type StringSlice []string 11 | 12 | // Set appends the string value to the list of values 13 | func (f *StringSlice) Set(value string) error { 14 | *f = append(*f, value) 15 | return nil 16 | } 17 | 18 | // String returns a readable representation of this value (for usage defaults) 19 | func (f *StringSlice) String() string { 20 | return strings.Join(*f, ",") 21 | } 22 | 23 | // Value returns the slice of strings set by this flag 24 | func (f *StringSlice) Value() []string { 25 | return *f 26 | } 27 | 28 | // Get returns the slice of strings set by this flag 29 | func (f *StringSlice) Get() interface{} { 30 | return *f 31 | } 32 | 33 | // StringSliceFlag is a flag with type *StringSlice 34 | type StringSliceFlag struct { 35 | Name string 36 | Usage string 37 | EnvVar string 38 | FilePath string 39 | Required bool 40 | Hidden bool 41 | TakesFile bool 42 | Value *StringSlice 43 | } 44 | 45 | // String returns a readable representation of this value 46 | // (for usage defaults) 47 | func (f StringSliceFlag) String() string { 48 | return FlagStringer(f) 49 | } 50 | 51 | // GetName returns the name of the flag 52 | func (f StringSliceFlag) GetName() string { 53 | return f.Name 54 | } 55 | 56 | // IsRequired returns whether or not the flag is required 57 | func (f StringSliceFlag) IsRequired() bool { 58 | return f.Required 59 | } 60 | 61 | // TakesValue returns true of the flag takes a value, otherwise false 62 | func (f StringSliceFlag) TakesValue() bool { 63 | return true 64 | } 65 | 66 | // GetUsage returns the usage string for the flag 67 | func (f StringSliceFlag) GetUsage() string { 68 | return f.Usage 69 | } 70 | 71 | // GetValue returns the flags value as string representation and an empty 72 | // string if the flag takes no value at all. 73 | func (f StringSliceFlag) GetValue() string { 74 | if f.Value != nil { 75 | return f.Value.String() 76 | } 77 | return "" 78 | } 79 | 80 | // Apply populates the flag given the flag set and environment 81 | // Ignores errors 82 | func (f StringSliceFlag) Apply(set *flag.FlagSet) { 83 | _ = f.ApplyWithError(set) 84 | } 85 | 86 | // ApplyWithError populates the flag given the flag set and environment 87 | func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { 88 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 89 | newVal := &StringSlice{} 90 | for _, s := range strings.Split(envVal, ",") { 91 | s = strings.TrimSpace(s) 92 | if err := newVal.Set(s); err != nil { 93 | return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) 94 | } 95 | } 96 | if f.Value == nil { 97 | f.Value = newVal 98 | } else { 99 | *f.Value = *newVal 100 | } 101 | } 102 | 103 | eachName(f.Name, func(name string) { 104 | if f.Value == nil { 105 | f.Value = &StringSlice{} 106 | } 107 | set.Var(f.Value, name, f.Usage) 108 | }) 109 | 110 | return nil 111 | } 112 | 113 | // StringSlice looks up the value of a local StringSliceFlag, returns 114 | // nil if not found 115 | func (c *Context) StringSlice(name string) []string { 116 | return lookupStringSlice(name, c.flagSet) 117 | } 118 | 119 | // GlobalStringSlice looks up the value of a global StringSliceFlag, returns 120 | // nil if not found 121 | func (c *Context) GlobalStringSlice(name string) []string { 122 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 123 | return lookupStringSlice(name, fs) 124 | } 125 | return nil 126 | } 127 | 128 | func lookupStringSlice(name string, set *flag.FlagSet) []string { 129 | f := set.Lookup(name) 130 | if f != nil { 131 | value, ok := f.Value.(*StringSlice) 132 | if !ok { 133 | return nil 134 | } 135 | // extract the slice from asserted value 136 | slice := value.Value() 137 | 138 | // extract default value from the flag 139 | var defaultVal []string 140 | for _, v := range strings.Split(f.DefValue, ",") { 141 | defaultVal = append(defaultVal, v) 142 | } 143 | 144 | // if the current value is not equal to the default value 145 | // remove the default values from the flag 146 | if !isStringSliceEqual(slice, defaultVal) { 147 | for _, v := range defaultVal { 148 | slice = removeFromStringSlice(slice, v) 149 | } 150 | } 151 | return slice 152 | } 153 | return nil 154 | } 155 | 156 | func removeFromStringSlice(slice []string, val string) []string { 157 | for i, v := range slice { 158 | if v == val { 159 | ret := append([]string{}, slice[:i]...) 160 | ret = append(ret, slice[i+1:]...) 161 | return ret 162 | } 163 | } 164 | return slice 165 | } 166 | 167 | func isStringSliceEqual(newValue, defaultValue []string) bool { 168 | // If one is nil, the other must also be nil. 169 | if (newValue == nil) != (defaultValue == nil) { 170 | return false 171 | } 172 | 173 | if len(newValue) != len(defaultValue) { 174 | return false 175 | } 176 | 177 | for i, v := range newValue { 178 | if v != defaultValue[i] { 179 | return false 180 | } 181 | } 182 | 183 | return true 184 | } 185 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_uint.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // UintFlag is a flag with type uint 10 | type UintFlag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value uint 18 | Destination *uint 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f UintFlag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f UintFlag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f UintFlag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f UintFlag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f UintFlag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // Apply populates the flag given the flag set and environment 48 | // Ignores errors 49 | func (f UintFlag) Apply(set *flag.FlagSet) { 50 | _ = f.ApplyWithError(set) 51 | } 52 | 53 | // ApplyWithError populates the flag given the flag set and environment 54 | func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { 55 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 56 | envValInt, err := strconv.ParseUint(envVal, 0, 64) 57 | if err != nil { 58 | return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) 59 | } 60 | 61 | f.Value = uint(envValInt) 62 | } 63 | 64 | eachName(f.Name, func(name string) { 65 | if f.Destination != nil { 66 | set.UintVar(f.Destination, name, f.Value, f.Usage) 67 | return 68 | } 69 | set.Uint(name, f.Value, f.Usage) 70 | }) 71 | 72 | return nil 73 | } 74 | 75 | // GetValue returns the flags value as string representation and an empty 76 | // string if the flag takes no value at all. 77 | func (f UintFlag) GetValue() string { 78 | return fmt.Sprintf("%d", f.Value) 79 | } 80 | 81 | // Uint looks up the value of a local UintFlag, returns 82 | // 0 if not found 83 | func (c *Context) Uint(name string) uint { 84 | return lookupUint(name, c.flagSet) 85 | } 86 | 87 | // GlobalUint looks up the value of a global UintFlag, returns 88 | // 0 if not found 89 | func (c *Context) GlobalUint(name string) uint { 90 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 91 | return lookupUint(name, fs) 92 | } 93 | return 0 94 | } 95 | 96 | func lookupUint(name string, set *flag.FlagSet) uint { 97 | f := set.Lookup(name) 98 | if f != nil { 99 | parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) 100 | if err != nil { 101 | return 0 102 | } 103 | return uint(parsed) 104 | } 105 | return 0 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_uint64.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Uint64Flag is a flag with type uint64 10 | type Uint64Flag struct { 11 | Name string 12 | Usage string 13 | EnvVar string 14 | FilePath string 15 | Required bool 16 | Hidden bool 17 | Value uint64 18 | Destination *uint64 19 | } 20 | 21 | // String returns a readable representation of this value 22 | // (for usage defaults) 23 | func (f Uint64Flag) String() string { 24 | return FlagStringer(f) 25 | } 26 | 27 | // GetName returns the name of the flag 28 | func (f Uint64Flag) GetName() string { 29 | return f.Name 30 | } 31 | 32 | // IsRequired returns whether or not the flag is required 33 | func (f Uint64Flag) IsRequired() bool { 34 | return f.Required 35 | } 36 | 37 | // TakesValue returns true of the flag takes a value, otherwise false 38 | func (f Uint64Flag) TakesValue() bool { 39 | return true 40 | } 41 | 42 | // GetUsage returns the usage string for the flag 43 | func (f Uint64Flag) GetUsage() string { 44 | return f.Usage 45 | } 46 | 47 | // GetValue returns the flags value as string representation and an empty 48 | // string if the flag takes no value at all. 49 | func (f Uint64Flag) GetValue() string { 50 | return fmt.Sprintf("%d", f.Value) 51 | } 52 | 53 | // Apply populates the flag given the flag set and environment 54 | // Ignores errors 55 | func (f Uint64Flag) Apply(set *flag.FlagSet) { 56 | _ = f.ApplyWithError(set) 57 | } 58 | 59 | // ApplyWithError populates the flag given the flag set and environment 60 | func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { 61 | if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { 62 | envValInt, err := strconv.ParseUint(envVal, 0, 64) 63 | if err != nil { 64 | return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) 65 | } 66 | 67 | f.Value = envValInt 68 | } 69 | 70 | eachName(f.Name, func(name string) { 71 | if f.Destination != nil { 72 | set.Uint64Var(f.Destination, name, f.Value, f.Usage) 73 | return 74 | } 75 | set.Uint64(name, f.Value, f.Usage) 76 | }) 77 | 78 | return nil 79 | } 80 | 81 | // Uint64 looks up the value of a local Uint64Flag, returns 82 | // 0 if not found 83 | func (c *Context) Uint64(name string) uint64 { 84 | return lookupUint64(name, c.flagSet) 85 | } 86 | 87 | // GlobalUint64 looks up the value of a global Uint64Flag, returns 88 | // 0 if not found 89 | func (c *Context) GlobalUint64(name string) uint64 { 90 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 91 | return lookupUint64(name, fs) 92 | } 93 | return 0 94 | } 95 | 96 | func lookupUint64(name string, set *flag.FlagSet) uint64 { 97 | f := set.Lookup(name) 98 | if f != nil { 99 | parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) 100 | if err != nil { 101 | return 0 102 | } 103 | return parsed 104 | } 105 | return 0 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // ExitErrHandlerFunc is executed if provided in order to handle ExitError values 27 | // returned by Actions and Before/After functions. 28 | type ExitErrHandlerFunc func(context *Context, err error) 29 | 30 | // FlagStringFunc is used by the help generation to display a flag, which is 31 | // expected to be a single line. 32 | type FlagStringFunc func(Flag) string 33 | 34 | // FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix 35 | // text for a flag's full name. 36 | type FlagNamePrefixFunc func(fullName, placeholder string) string 37 | 38 | // FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help 39 | // with the environment variable details. 40 | type FlagEnvHintFunc func(envVar, str string) string 41 | 42 | // FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help 43 | // with the file path details. 44 | type FlagFileHintFunc func(filePath, str string) string 45 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/parse.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "strings" 6 | ) 7 | 8 | type iterativeParser interface { 9 | newFlagSet() (*flag.FlagSet, error) 10 | useShortOptionHandling() bool 11 | } 12 | 13 | // To enable short-option handling (e.g., "-it" vs "-i -t") we have to 14 | // iteratively catch parsing errors. This way we achieve LR parsing without 15 | // transforming any arguments. Otherwise, there is no way we can discriminate 16 | // combined short options from common arguments that should be left untouched. 17 | // Pass `shellComplete` to continue parsing options on failure during shell 18 | // completion when, the user-supplied options may be incomplete. 19 | func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error { 20 | for { 21 | err := set.Parse(args) 22 | if !ip.useShortOptionHandling() || err == nil { 23 | if shellComplete { 24 | return nil 25 | } 26 | return err 27 | } 28 | 29 | errStr := err.Error() 30 | trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") 31 | if errStr == trimmed { 32 | return err 33 | } 34 | 35 | // regenerate the initial args with the split short opts 36 | argsWereSplit := false 37 | for i, arg := range args { 38 | // skip args that are not part of the error message 39 | if name := strings.TrimLeft(arg, "-"); name != trimmed { 40 | continue 41 | } 42 | 43 | // if we can't split, the error was accurate 44 | shortOpts := splitShortOptions(set, arg) 45 | if len(shortOpts) == 1 { 46 | return err 47 | } 48 | 49 | // swap current argument with the split version 50 | args = append(args[:i], append(shortOpts, args[i+1:]...)...) 51 | argsWereSplit = true 52 | break 53 | } 54 | 55 | // This should be an impossible to reach code path, but in case the arg 56 | // splitting failed to happen, this will prevent infinite loops 57 | if !argsWereSplit { 58 | return err 59 | } 60 | 61 | // Since custom parsing failed, replace the flag set before retrying 62 | newSet, err := ip.newFlagSet() 63 | if err != nil { 64 | return err 65 | } 66 | *set = *newSet 67 | } 68 | } 69 | 70 | func splitShortOptions(set *flag.FlagSet, arg string) []string { 71 | shortFlagsExist := func(s string) bool { 72 | for _, c := range s[1:] { 73 | if f := set.Lookup(string(c)); f == nil { 74 | return false 75 | } 76 | } 77 | return true 78 | } 79 | 80 | if !isSplittable(arg) || !shortFlagsExist(arg) { 81 | return []string{arg} 82 | } 83 | 84 | separated := make([]string, 0, len(arg)-1) 85 | for _, flagChar := range arg[1:] { 86 | separated = append(separated, "-"+string(flagChar)) 87 | } 88 | 89 | return separated 90 | } 91 | 92 | func isSplittable(flagArg string) bool { 93 | return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 94 | } 95 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/sort.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import "unicode" 4 | 5 | // lexicographicLess compares strings alphabetically considering case. 6 | func lexicographicLess(i, j string) bool { 7 | iRunes := []rune(i) 8 | jRunes := []rune(j) 9 | 10 | lenShared := len(iRunes) 11 | if lenShared > len(jRunes) { 12 | lenShared = len(jRunes) 13 | } 14 | 15 | for index := 0; index < lenShared; index++ { 16 | ir := iRunes[index] 17 | jr := jRunes[index] 18 | 19 | if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { 20 | return lir < ljr 21 | } 22 | 23 | if ir != jr { 24 | return ir < jr 25 | } 26 | } 27 | 28 | return i < j 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/template.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // AppHelpTemplate is the text template for the Default help topic. 4 | // cli.go uses text/template to render templates. You can 5 | // render custom help text by setting this variable. 6 | var AppHelpTemplate = `NAME: 7 | {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} 8 | 9 | USAGE: 10 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} 11 | 12 | VERSION: 13 | {{.Version}}{{end}}{{end}}{{if .Description}} 14 | 15 | DESCRIPTION: 16 | {{.Description}}{{end}}{{if len .Authors}} 17 | 18 | AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: 19 | {{range $index, $author := .Authors}}{{if $index}} 20 | {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} 21 | 22 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 23 | 24 | {{.Name}}:{{range .VisibleCommands}} 25 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} 26 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 27 | 28 | GLOBAL OPTIONS: 29 | {{range $index, $option := .VisibleFlags}}{{if $index}} 30 | {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} 31 | 32 | COPYRIGHT: 33 | {{.Copyright}}{{end}} 34 | ` 35 | 36 | // CommandHelpTemplate is the text template for the command help topic. 37 | // cli.go uses text/template to render templates. You can 38 | // render custom help text by setting this variable. 39 | var CommandHelpTemplate = `NAME: 40 | {{.HelpName}} - {{.Usage}} 41 | 42 | USAGE: 43 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} 44 | 45 | CATEGORY: 46 | {{.Category}}{{end}}{{if .Description}} 47 | 48 | DESCRIPTION: 49 | {{.Description}}{{end}}{{if .VisibleFlags}} 50 | 51 | OPTIONS: 52 | {{range .VisibleFlags}}{{.}} 53 | {{end}}{{end}} 54 | ` 55 | 56 | // SubcommandHelpTemplate is the text template for the subcommand help topic. 57 | // cli.go uses text/template to render templates. You can 58 | // render custom help text by setting this variable. 59 | var SubcommandHelpTemplate = `NAME: 60 | {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} 61 | 62 | USAGE: 63 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} 64 | 65 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 66 | 67 | {{.Name}}:{{range .VisibleCommands}} 68 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} 69 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 70 | 71 | OPTIONS: 72 | {{range .VisibleFlags}}{{.}} 73 | {{end}}{{end}} 74 | ` 75 | 76 | var MarkdownDocTemplate = `% {{ .App.Name }}(8) {{ .App.Description }} 77 | 78 | % {{ .App.Author }} 79 | 80 | # NAME 81 | 82 | {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} 83 | 84 | # SYNOPSIS 85 | 86 | {{ .App.Name }} 87 | {{ if .SynopsisArgs }} 88 | ` + "```" + ` 89 | {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` 90 | {{ end }}{{ if .App.UsageText }} 91 | # DESCRIPTION 92 | 93 | {{ .App.UsageText }} 94 | {{ end }} 95 | **Usage**: 96 | 97 | ` + "```" + ` 98 | {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] 99 | ` + "```" + ` 100 | {{ if .GlobalArgs }} 101 | # GLOBAL OPTIONS 102 | {{ range $v := .GlobalArgs }} 103 | {{ $v }}{{ end }} 104 | {{ end }}{{ if .Commands }} 105 | # COMMANDS 106 | {{ range $v := .Commands }} 107 | {{ $v }}{{ end }}{{ end }}` 108 | 109 | var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion 110 | 111 | function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' 112 | for i in (commandline -opc) 113 | if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} 114 | return 1 115 | end 116 | end 117 | return 0 118 | end 119 | 120 | {{ range $v := .Completions }}{{ $v }} 121 | {{ end }}` 122 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This project is covered by two different licenses: MIT and Apache. 3 | 4 | #### MIT License #### 5 | 6 | The following files were ported to Go from C files of libyaml, and thus 7 | are still covered by their original MIT license, with the additional 8 | copyright staring in 2011 when the project was ported over: 9 | 10 | apic.go emitterc.go parserc.go readerc.go scannerc.go 11 | writerc.go yamlh.go yamlprivateh.go 12 | 13 | Copyright (c) 2006-2010 Kirill Simonov 14 | Copyright (c) 2006-2011 Kirill Simonov 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of 17 | this software and associated documentation files (the "Software"), to deal in 18 | the Software without restriction, including without limitation the rights to 19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 20 | of the Software, and to permit persons to whom the Software is furnished to do 21 | so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | 34 | ### Apache License ### 35 | 36 | All the remaining project files are covered by the Apache license: 37 | 38 | Copyright (c) 2011-2019 Canonical Ltd 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.2, but preserves some behavior 16 | from 1.1 for backwards compatibility. 17 | 18 | Specifically, as of v3 of the yaml package: 19 | 20 | - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being 21 | decoded into a typed bool value. Otherwise they behave as a string. Booleans 22 | in YAML 1.2 are _true/false_ only. 23 | - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ 24 | as specified in YAML 1.2, because most parsers still use the old format. 25 | Octals in the _0o777_ format are supported though, so new files work. 26 | - Does not support base-60 floats. These are gone from YAML 1.2, and were 27 | actually never supported by this package as it's clearly a poor choice. 28 | 29 | and offers backwards 30 | compatibility with YAML 1.1 in some cases. 31 | 1.2, including support for 32 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 33 | implemented, and base-60 floats from YAML 1.1 are purposefully not 34 | supported since they're a poor design and are gone in YAML 1.2. 35 | 36 | Installation and usage 37 | ---------------------- 38 | 39 | The import path for the package is *gopkg.in/yaml.v3*. 40 | 41 | To install it, run: 42 | 43 | go get gopkg.in/yaml.v3 44 | 45 | API documentation 46 | ----------------- 47 | 48 | If opened in a browser, the import path itself leads to the API documentation: 49 | 50 | - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) 51 | 52 | API stability 53 | ------------- 54 | 55 | The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). 56 | 57 | 58 | License 59 | ------- 60 | 61 | The yaml package is licensed under the MIT and Apache License 2.0 licenses. 62 | Please see the LICENSE file for details. 63 | 64 | 65 | Example 66 | ------- 67 | 68 | ```Go 69 | package main 70 | 71 | import ( 72 | "fmt" 73 | "log" 74 | 75 | "gopkg.in/yaml.v3" 76 | ) 77 | 78 | var data = ` 79 | a: Easy! 80 | b: 81 | c: 2 82 | d: [3, 4] 83 | ` 84 | 85 | // Note: struct fields must be public in order for unmarshal to 86 | // correctly populate the data. 87 | type T struct { 88 | A string 89 | B struct { 90 | RenamedC int `yaml:"c"` 91 | D []int `yaml:",flow"` 92 | } 93 | } 94 | 95 | func main() { 96 | t := T{} 97 | 98 | err := yaml.Unmarshal([]byte(data), &t) 99 | if err != nil { 100 | log.Fatalf("error: %v", err) 101 | } 102 | fmt.Printf("--- t:\n%v\n\n", t) 103 | 104 | d, err := yaml.Marshal(&t) 105 | if err != nil { 106 | log.Fatalf("error: %v", err) 107 | } 108 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 109 | 110 | m := make(map[interface{}]interface{}) 111 | 112 | err = yaml.Unmarshal([]byte(data), &m) 113 | if err != nil { 114 | log.Fatalf("error: %v", err) 115 | } 116 | fmt.Printf("--- m:\n%v\n\n", m) 117 | 118 | d, err = yaml.Marshal(&m) 119 | if err != nil { 120 | log.Fatalf("error: %v", err) 121 | } 122 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 123 | } 124 | ``` 125 | 126 | This example will generate the following output: 127 | 128 | ``` 129 | --- t: 130 | {Easy! {2 [3 4]}} 131 | 132 | --- t dump: 133 | a: Easy! 134 | b: 135 | c: 2 136 | d: [3, 4] 137 | 138 | 139 | --- m: 140 | map[a:Easy! b:map[c:2 d:[3 4]]] 141 | 142 | --- m dump: 143 | a: Easy! 144 | b: 145 | c: 2 146 | d: 147 | - 3 148 | - 4 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/sorter.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "reflect" 20 | "unicode" 21 | ) 22 | 23 | type keyList []reflect.Value 24 | 25 | func (l keyList) Len() int { return len(l) } 26 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 27 | func (l keyList) Less(i, j int) bool { 28 | a := l[i] 29 | b := l[j] 30 | ak := a.Kind() 31 | bk := b.Kind() 32 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 33 | a = a.Elem() 34 | ak = a.Kind() 35 | } 36 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 37 | b = b.Elem() 38 | bk = b.Kind() 39 | } 40 | af, aok := keyFloat(a) 41 | bf, bok := keyFloat(b) 42 | if aok && bok { 43 | if af != bf { 44 | return af < bf 45 | } 46 | if ak != bk { 47 | return ak < bk 48 | } 49 | return numLess(a, b) 50 | } 51 | if ak != reflect.String || bk != reflect.String { 52 | return ak < bk 53 | } 54 | ar, br := []rune(a.String()), []rune(b.String()) 55 | digits := false 56 | for i := 0; i < len(ar) && i < len(br); i++ { 57 | if ar[i] == br[i] { 58 | digits = unicode.IsDigit(ar[i]) 59 | continue 60 | } 61 | al := unicode.IsLetter(ar[i]) 62 | bl := unicode.IsLetter(br[i]) 63 | if al && bl { 64 | return ar[i] < br[i] 65 | } 66 | if al || bl { 67 | if digits { 68 | return al 69 | } else { 70 | return bl 71 | } 72 | } 73 | var ai, bi int 74 | var an, bn int64 75 | if ar[i] == '0' || br[i] == '0' { 76 | for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 77 | if ar[j] != '0' { 78 | an = 1 79 | bn = 1 80 | break 81 | } 82 | } 83 | } 84 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 85 | an = an*10 + int64(ar[ai]-'0') 86 | } 87 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 88 | bn = bn*10 + int64(br[bi]-'0') 89 | } 90 | if an != bn { 91 | return an < bn 92 | } 93 | if ai != bi { 94 | return ai < bi 95 | } 96 | return ar[i] < br[i] 97 | } 98 | return len(ar) < len(br) 99 | } 100 | 101 | // keyFloat returns a float value for v if it is a number/bool 102 | // and whether it is a number/bool or not. 103 | func keyFloat(v reflect.Value) (f float64, ok bool) { 104 | switch v.Kind() { 105 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 | return float64(v.Int()), true 107 | case reflect.Float32, reflect.Float64: 108 | return v.Float(), true 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | return float64(v.Uint()), true 111 | case reflect.Bool: 112 | if v.Bool() { 113 | return 1, true 114 | } 115 | return 0, true 116 | } 117 | return 0, false 118 | } 119 | 120 | // numLess returns whether a < b. 121 | // a and b must necessarily have the same kind. 122 | func numLess(a, b reflect.Value) bool { 123 | switch a.Kind() { 124 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 125 | return a.Int() < b.Int() 126 | case reflect.Float32, reflect.Float64: 127 | return a.Float() < b.Float() 128 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 129 | return a.Uint() < b.Uint() 130 | case reflect.Bool: 131 | return !a.Bool() && b.Bool() 132 | } 133 | panic("not a number") 134 | } 135 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v3/writerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // 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. 22 | 23 | package yaml 24 | 25 | // Set the writer error and return false. 26 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 27 | emitter.error = yaml_WRITER_ERROR 28 | emitter.problem = problem 29 | return false 30 | } 31 | 32 | // Flush the output buffer. 33 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 34 | if emitter.write_handler == nil { 35 | panic("write handler not set") 36 | } 37 | 38 | // Check if the buffer is empty. 39 | if emitter.buffer_pos == 0 { 40 | return true 41 | } 42 | 43 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 44 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 45 | } 46 | emitter.buffer_pos = 0 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # filippo.io/edwards25519 v1.1.0 2 | ## explicit; go 1.20 3 | filippo.io/edwards25519 4 | filippo.io/edwards25519/field 5 | # github.com/cpuguy83/go-md2man/v2 v2.0.7 6 | ## explicit; go 1.12 7 | github.com/cpuguy83/go-md2man/v2/md2man 8 | # github.com/davecgh/go-spew v1.1.1 9 | ## explicit 10 | github.com/davecgh/go-spew/spew 11 | # github.com/go-sql-driver/mysql v1.9.3 12 | ## explicit; go 1.21.0 13 | github.com/go-sql-driver/mysql 14 | # github.com/pmezard/go-difflib v1.0.0 15 | ## explicit 16 | github.com/pmezard/go-difflib/difflib 17 | # github.com/russross/blackfriday/v2 v2.1.0 18 | ## explicit 19 | github.com/russross/blackfriday/v2 20 | # github.com/stretchr/testify v1.11.1 21 | ## explicit; go 1.17 22 | github.com/stretchr/testify/assert 23 | github.com/stretchr/testify/assert/yaml 24 | # github.com/urfave/cli v1.22.17 25 | ## explicit; go 1.11 26 | github.com/urfave/cli 27 | # gopkg.in/yaml.v3 v3.0.1 28 | ## explicit 29 | gopkg.in/yaml.v3 30 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": "TBD", 3 | "commit": "TBD", 4 | "source": "https://github.com/mozilla-services/go-bouncer", 5 | "version": "TBD" 6 | } 7 | --------------------------------------------------------------------------------