├── .dockerignore ├── .editorconfig ├── .envrc ├── .github ├── CODEOWNERS ├── assets │ └── demo.mov ├── config │ ├── .hadolint.yaml │ ├── .releaserc.json │ └── .yamllint.yaml ├── docs │ └── examples │ │ └── black-cover.jpg ├── renovate.json ├── taskfiles │ └── pre-commit.yaml └── workflows │ ├── _goreleaser.yml │ ├── _quality.yml │ └── _release.yml ├── .gitignore ├── .go-version ├── .golangci.yml ├── .goreleaser.yaml ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── Taskfile.yaml ├── cmd └── loli │ └── main.go ├── commands ├── cmd.go ├── completion.go ├── root.go ├── search.go ├── search_file.go ├── search_link.go ├── search_usage.go ├── upgrade.go └── version.go ├── devbox.json ├── devbox.lock ├── go.mod ├── go.sum ├── internal ├── api │ ├── client.go │ └── types.go ├── cli │ ├── asset.go │ ├── cli.go │ ├── release.go │ └── types.go ├── consts │ └── consts.go ├── debug │ └── debug.go ├── log │ ├── logger.go │ ├── logger_config.go │ ├── logger_formatters.go │ ├── logger_messages.go │ ├── logger_options.go │ └── logrus.go ├── trace │ ├── search_file.go │ ├── search_link.go │ ├── search_usage.go │ └── signals.go ├── types │ └── response.go ├── utils │ └── utils.go └── version │ └── version.go └── scripts └── completions.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | LICENSE 3 | README.md 4 | dist 5 | completions 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # This is the project's root directory 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | # The files are utf-8 encoded 9 | charset = utf-8 10 | # Each indent should contain 2 spaces 11 | indent_size = 2 12 | # Use Unix line endings 13 | end_of_line = lf 14 | # Use spaces for indentation 15 | indent_style = space 16 | # A file must end with an empty line - this is good for version control systems 17 | insert_final_newline = true 18 | # No whitespace at the end of line 19 | trim_trailing_whitespace = true 20 | 21 | # JSON files 22 | [*.json] 23 | # Each indent should contain 4 spaces for Python files 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # Devbox Support 2 | # This script checks if devbox is available, generates direnv configuration, and exits if successful 3 | if has devbox; then 4 | eval "$(devbox generate direnv --print-envrc)" 5 | exit 0 6 | else 7 | echo "Devbox is not installed. Please install it to proceed." 8 | exit 1 9 | fi 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @lpsm-dev 2 | -------------------------------------------------------------------------------- /.github/assets/demo.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpsm-dev/loli/fe8d0f49d8fe3786e3d4ef56bb56580a6ce36b8a/.github/assets/demo.mov -------------------------------------------------------------------------------- /.github/config/.hadolint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignored: 3 | - DL3006 4 | - DL3008 5 | -------------------------------------------------------------------------------- /.github/config/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main" 4 | ], 5 | "tagFormat": "v${version}", 6 | "plugins": [ 7 | ["@semantic-release/commit-analyzer", { 8 | "preset": "conventionalcommits", 9 | "releaseRules": [ 10 | { "type": "build", "release": "patch" }, 11 | { "type": "docs", "release": "patch" }, 12 | { "type": "ci", "release": "patch" }, 13 | { "type": "feat", "release": "minor" }, 14 | { "type": "fix", "release": "patch" }, 15 | { "type": "perf", "release": "patch" }, 16 | { "type": "refactor", "release": "patch" }, 17 | { "type": "style", "release": "patch" }, 18 | { "type": "test", "release": "patch" }, 19 | { "revert": true, "release": "patch" }, 20 | { "breaking": true, "release": "major" } 21 | ], 22 | "parserOpts": { 23 | "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"] 24 | } 25 | }], 26 | ["@semantic-release/release-notes-generator", { 27 | "preset": "conventionalcommits", 28 | "presetConfig": { 29 | "types": [ 30 | { "type": "build", "section": ":nut_and_bolt: Build", "hidden": false }, 31 | { "type": "ci", "section": ":repeat: CI", "hidden": false }, 32 | { "type": "docs", "section": ":memo: Docs", "hidden": false }, 33 | { "type": "feat", "section": ":sparkles: News", "hidden": false }, 34 | { "type": "fix", "section": ":bug: Fix", "hidden": false }, 35 | { "type": "perf", "section": ":fast_forward: Performance", "hidden": false }, 36 | { "type": "refactor", "section": ":zap: Refact", "hidden": false }, 37 | { "type": "revert", "section": ":flashlight: Revert", "hidden": false }, 38 | { "type": "style", "section": ":barber: Style", "hidden": false }, 39 | { "type": "test", "section": ":white_check_mark: Test", "hidden": false } 40 | ]} 41 | }], 42 | ["@semantic-release/github", { 43 | "addReleases": "top" 44 | }], 45 | ["@semantic-release/changelog", { 46 | "changelogFile": "CHANGELOG.md", 47 | "changelogTitle": "# Semantic Versioning Changelog" 48 | }], 49 | ["@semantic-release/git", { 50 | "assets": ["CHANGELOG.md", "README.md"], 51 | "message": "chore(release): version <%= nextRelease.version %> - <%= new Date().toLocaleDateString('en-US', {year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }) %>" 52 | }] 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.github/config/.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | ignore: node_modules 4 | rules: 5 | new-lines: disable 6 | line-length: 7 | max: 300 8 | level: warning 9 | indentation: 10 | spaces: 2 11 | indent-sequences: true 12 | comments-indentation: disable 13 | truthy: 14 | check-keys: false 15 | -------------------------------------------------------------------------------- /.github/docs/examples/black-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lpsm-dev/loli/fe8d0f49d8fe3786e3d4ef56bb56580a6ce36b8a/.github/docs/examples/black-cover.jpg -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base", ":disableDependencyDashboard"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/taskfiles/pre-commit.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | tasks: 5 | init: 6 | desc: Initialize pre-commit hooks 7 | preconditions: 8 | - sh: which pre-commit 9 | msg: kind {{.PATH_ERROR}} 10 | cmds: 11 | - pre-commit clean 12 | - pre-commit gc 13 | - task: update 14 | - pre-commit install --install-hooks -t commit-msg 15 | 16 | update: 17 | desc: Update pre-commit hooks 18 | cmds: 19 | - pre-commit autoupdate 20 | 21 | delete: 22 | desc: Delete pre-commit hooks 23 | cmds: 24 | - pre-commit uninstall --hook-type commit-msg 25 | 26 | run: 27 | desc: Run pre-commit hooks 28 | cmds: 29 | - pre-commit run --all-files 30 | -------------------------------------------------------------------------------- /.github/workflows/_goreleaser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: GoReleaser 3 | 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | goreleaser: 14 | name: GoReleaser 15 | runs-on: ubuntu-24.04 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4.2.2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Get Details 23 | id: get_details 24 | run: | 25 | echo ::set-output name=SOURCE_NAME::${GITHUB_REF#refs/*/} 26 | echo ::set-output name=SOURCE_BRANCH::${GITHUB_REF#refs/heads/} 27 | echo ::set-output name=SOURCE_TAG::$(git tag --sort=committerdate | tail -1) 28 | echo ::set-output name=GOVERSION::$(cat .go-version) 29 | 30 | - name: List tags 31 | run: | 32 | git tag --list 33 | 34 | - name: Show variables 35 | run: | 36 | echo $SOURCE_NAME 37 | echo $SOURCE_BRANCH 38 | echo $SOURCE_TAG 39 | env: 40 | SOURCE_NAME: ${{ steps.get_details.outputs.SOURCE_NAME }} 41 | SOURCE_BRANCH: ${{ steps.get_details.outputs.SOURCE_BRANCH }} 42 | SOURCE_TAG: ${{ steps.get_details.outputs.SOURCE_TAG }} 43 | 44 | - name: Set up Go 45 | uses: actions/setup-go@v5 46 | with: 47 | go-version: ${{ steps.get_details.outputs.GOVERSION }} 48 | 49 | - name: Run GoReleaser 50 | uses: goreleaser/goreleaser-action@v6 51 | with: 52 | args: release --rm-dist 53 | env: 54 | CLIENT_VERSION: ${{ steps.get_details.outputs.SOURCE_TAG }} 55 | GO_VERSION: ${{ steps.get_details.outputs.GOVERSION }} 56 | GORELEASER_CURRENT_TAG: ${{ steps.get_details.outputs.SOURCE_TAG }} 57 | GIT_BRANCH: ${{ steps.get_details.outputs.SOURCE_BRANCH }} 58 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 59 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/_quality.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | 8 | jobs: 9 | test: 10 | name: Test 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - name: Check out code from GitHub 14 | uses: actions/checkout@v4.2.2 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Commit Lint 19 | uses: wagoid/commitlint-github-action@v6 20 | continue-on-error: true 21 | 22 | - name: Secret Detection 23 | uses: gitleaks/gitleaks-action@v2 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | GITLEAKS_ENABLE_COMMENTS: true 27 | continue-on-error: false 28 | 29 | - name: Hadolint 30 | uses: hadolint/hadolint-action@v3.1.0 31 | with: 32 | dockerfile: Dockerfile 33 | config: ${{ github.workspace }}/.github/config/.hadolint.yaml 34 | -------------------------------------------------------------------------------- /.github/workflows/_release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: SemanticRelease 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | git-ref: 8 | description: Git Branch Reference 9 | default: main 10 | required: true 11 | 12 | jobs: 13 | release: 14 | name: Release 15 | runs-on: ubuntu-24.04 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | persist-credentials: false 22 | if: github.event.inputs.git-ref == 'main' 23 | 24 | - name: Setup node 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: 'lts/*' 28 | if: github.event.inputs.git-ref == 'main' 29 | 30 | - name: Copy rules 31 | run: | 32 | cp .github/config/.releaserc.json . 33 | 34 | - name: Install dependencies 35 | run: npm install 36 | if: github.event.inputs.git-ref == 'main' 37 | 38 | - name: Release 39 | run: npx semantic-release 40 | env: 41 | CI: true 42 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 43 | if: github.event.inputs.git-ref == 'main' 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ================================================ 2 | # GENERAL 3 | # ================================================ 4 | 5 | # Ignore IDEs 6 | .idea 7 | 8 | # Ignore OS files 9 | .DS_Store 10 | 11 | # Packages 12 | *.tgz 13 | 14 | # Secrets path 15 | .secrets/ 16 | 17 | # General reports 18 | /report 19 | /reports 20 | .dccache 21 | 22 | # Dot env 23 | .env 24 | 25 | # General folders 26 | completions 27 | 28 | # ================================================ 29 | # VSCODE 30 | # ================================================ 31 | 32 | .vscode/* 33 | !.vscode/settings.json 34 | !.vscode/tasks.json 35 | !.vscode/launch.json 36 | !.vscode/extensions.json 37 | *.code-workspace 38 | 39 | # Stores VSCode versions used for testing VSCode extensions 40 | .vscode-test 41 | 42 | # ================================================ 43 | # GOLANG 44 | # ================================================ 45 | 46 | # Binaries for programs and plugins 47 | *.exe 48 | *.exe~ 49 | *.dll 50 | *.so 51 | *.dylib 52 | 53 | # Test binary, built with `go test -c` 54 | *.test 55 | 56 | # Output of the go coverage tool, specifically when used with LiteIDE 57 | *.out 58 | 59 | # Dependency directories (remove the comment below to include it) 60 | # vendor/ 61 | 62 | ### Go Patch ### 63 | vendor/ 64 | Godeps/ 65 | bin/ 66 | dist/ 67 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.23.5 2 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | run: 3 | timeout: 10m 4 | tests: true 5 | linters: 6 | enable-all: false 7 | enable: 8 | - bodyclose 9 | - errcheck 10 | - gofmt 11 | - goimports 12 | - govet 13 | - gosimple 14 | - ineffassign 15 | - misspell 16 | - nilerr 17 | - staticcheck 18 | - stylecheck 19 | - typecheck 20 | - unconvert 21 | - unparam 22 | - unused 23 | - whitespace 24 | disable: 25 | - deadcode 26 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | project_name: loli 3 | env: 4 | - GO111MODULE=on 5 | before: 6 | hooks: 7 | - make clean 8 | - go mod tidy 9 | - go generate ./... 10 | builds: 11 | - id: loli 12 | binary: loli 13 | env: 14 | - CGO_ENABLED=0 15 | main: ./cmd/loli/main.go 16 | goos: 17 | - linux 18 | - darwin 19 | - windows 20 | goarch: 21 | - amd64 22 | - arm64 23 | - ppc64le 24 | mod_timestamp: "{{.CommitTimestamp}}" 25 | flags: 26 | - -trimpath 27 | ldflags: 28 | - -s -w 29 | - -X "github.com/ci-monk/loli/internal/version.builtDate={{.CommitDate}}" 30 | - -X "github.com/ci-monk/loli/internal/version.builtBy=goreleaser" 31 | - -X "github.com/ci-monk/loli/internal/version.cliVersion={{.Env.CLIENT_VERSION}}" 32 | - -X "github.com/ci-monk/loli/internal/version.commit={{.Commit}}" 33 | - -X "github.com/ci-monk/loli/internal/version.commitShort={{.ShortCommit}}" 34 | - -X "github.com/ci-monk/loli/internal/version.commitBranch={{.Env.GIT_BRANCH}}" 35 | - -X "github.com/ci-monk/loli/internal/version.goVersion={{.Env.GO_VERSION}}" 36 | archives: 37 | - name_template: "{{.ProjectName}}_v{{.Version}}_{{.Os}}-{{.Arch}}" 38 | replacements: 39 | darwin: Darwin 40 | linux: Linux 41 | windows: Windows 42 | amd64: x86_64 43 | format_overrides: 44 | - goos: windows 45 | format: zip 46 | files: 47 | - LICENSE 48 | - README.md 49 | snapshot: 50 | name_template: "SNAPSHOT-{{ .Commit }}" 51 | checksum: 52 | name_template: "{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS" 53 | algorithm: sha256 54 | changelog: 55 | skip: true 56 | brews: 57 | - name: loli 58 | tap: 59 | owner: ci-monk 60 | branch: main 61 | name: homebrew-tools 62 | folder: formula 63 | commit_author: 64 | name: ci-monk 65 | email: ci-monk@protonmail.com 66 | commit_msg_template: "feat: brew formula update for {{ .ProjectName }} version {{ .Tag }}" 67 | homepage: "https://github.com/ci-monk/loli" 68 | description: "Install Loli CLI with brew and find animes passing images" 69 | license: "MIT" 70 | install: | 71 | bin.install "loli" 72 | release: 73 | github: 74 | owner: ci-monk 75 | name: loli 76 | name_template: "v{{.Version}}" 77 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.4.0 5 | hooks: 6 | - id: check-json 7 | - id: check-case-conflict 8 | - id: check-merge-conflict 9 | - id: check-docstring-first 10 | - id: check-executables-have-shebangs 11 | - id: debug-statements 12 | - id: fix-byte-order-marker 13 | - id: fix-encoding-pragma 14 | - id: mixed-line-ending 15 | - id: trailing-whitespace 16 | args: [--markdown-linebreak-ext=md] 17 | - repo: https://github.com/zricethezav/gitleaks 18 | rev: v8.17.0 19 | hooks: 20 | - id: gitleaks 21 | args: ["--verbose"] 22 | - repo: https://github.com/commitizen-tools/commitizen 23 | rev: 3.5.3 24 | hooks: 25 | - id: commitizen 26 | - id: commitizen-branch 27 | stages: [push] 28 | - repo: https://github.com/Lucas-C/pre-commit-hooks 29 | rev: v1.5.1 30 | hooks: 31 | - id: remove-crlf 32 | - id: remove-tabs 33 | - repo: https://github.com/sirosen/fix-smartquotes 34 | rev: 0.2.0 35 | hooks: 36 | - id: fix-smartquotes 37 | - repo: local 38 | hooks: 39 | - id: go-setup 40 | name: Go Setup 41 | language: system 42 | entry: make 43 | args: ["setup"] 44 | types: ["go"] 45 | pass_filenames: false 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Semantic Versioning Changelog 2 | 3 | ## [1.15.0](https://github.com/ci-monk/loli/compare/v1.14.2...v1.15.0) (2022-10-04) 4 | 5 | 6 | ### :memo: Docs 7 | 8 | * change .mov ([38b2c39](https://github.com/ci-monk/loli/commit/38b2c390d5ce689ae224844edb2d07af7e161dee)) 9 | * change title ([9a3615e](https://github.com/ci-monk/loli/commit/9a3615e9158b8f43a93e6289cccfb0a7708f5336)) 10 | * demo location ([f495bed](https://github.com/ci-monk/loli/commit/f495bed55d55e879f26fcdee683193b835579bc1)) 11 | * desc first ([5ae3dca](https://github.com/ci-monk/loli/commit/5ae3dcadb8c850204a32ff9dc458a9d8309b6c63)) 12 | * examples usage ([5721c39](https://github.com/ci-monk/loli/commit/5721c3968b9dfb07582d064714420e298c65b53e)) 13 | * pretty readme changes install ([b0a92fc](https://github.com/ci-monk/loli/commit/b0a92fc217b3c03daa94a1a140d86fd5c86ff46d)) 14 | * remove content ([5e4f7f3](https://github.com/ci-monk/loli/commit/5e4f7f3e95ba635a510fefd23e00f64ffada3f79)) 15 | * remove development with docker ([2a02df8](https://github.com/ci-monk/loli/commit/2a02df86ca7aec3a5da9ea9b4f464f362aaa87e4)) 16 | * remove topic ([0450b6a](https://github.com/ci-monk/loli/commit/0450b6a240108b27ad477413e6a648e4f88da68c)) 17 | * remove visuals topic ([647c6fa](https://github.com/ci-monk/loli/commit/647c6fa85495fd38c0eb4a7f5142f6349ae5326a)) 18 | * rollback topic ([d1b35a8](https://github.com/ci-monk/loli/commit/d1b35a81ccb0f1234bc9c3000ff822b21a6fa83c)) 19 | * sections option ([befebda](https://github.com/ci-monk/loli/commit/befebdad4c387770173f64d5f603a0b7265273f1)) 20 | * sort topics ([fb653a3](https://github.com/ci-monk/loli/commit/fb653a355ac1414168d43a515673a2ed140cbc87)) 21 | * upload .mov ([0b6dd07](https://github.com/ci-monk/loli/commit/0b6dd0795254735f45414ba47f77dfe67ae126ae)) 22 | 23 | 24 | ### :sparkles: News 25 | 26 | * add demo.mov ([0cf35c4](https://github.com/ci-monk/loli/commit/0cf35c477993d8bddaf41ee5e42ec0d4a6c71a07)) 27 | * add golangci setup and others ([f7af46f](https://github.com/ci-monk/loli/commit/f7af46f9d092eb517fb78088066841a9e3fef733)) 28 | * add scripts folder ([87fa760](https://github.com/ci-monk/loli/commit/87fa760316b8b33d526486e7679f437e171f9738)) 29 | 30 | 31 | ### :bug: Fix 32 | 33 | * brew setup, golangci and make commands ([7dd5f11](https://github.com/ci-monk/loli/commit/7dd5f1168b23978b93bff534f10eab43ce600123)) 34 | * **deps:** update module github.com/pterm/pterm to v0.12.49 ([02a721b](https://github.com/ci-monk/loli/commit/02a721b23c726e48503e0525ec42cc04b4e950f6)) 35 | * folder examples name ([bda3cbd](https://github.com/ci-monk/loli/commit/bda3cbdd56f7b5f95f01954b43db0333b54eb271)) 36 | * go release remove rules ([de2f7f2](https://github.com/ci-monk/loli/commit/de2f7f2b9036aac68bf9350d4a218d67d25fc54a)) 37 | * golang ci linters ([959fa00](https://github.com/ci-monk/loli/commit/959fa006d284a1947eb0d1df2a4aa1b5d26f9083)) 38 | * remove images ([6d0ca59](https://github.com/ci-monk/loli/commit/6d0ca5964e1188046b69d02847b091b69d0e42e2)) 39 | * setup dockerfile to dev ([e60bb5c](https://github.com/ci-monk/loli/commit/e60bb5c0b6ac2706c541538f0bda3722454a2523)) 40 | 41 | ## [1.14.2](https://github.com/ci-monk/loli/compare/v1.14.1...v1.14.2) (2022-09-27) 42 | 43 | 44 | ### :memo: Docs 45 | 46 | * change readme ([6cdf142](https://github.com/ci-monk/loli/commit/6cdf142fb5c8521f40425cd47e77eb958231cc19)) 47 | * rename chars ([93f7465](https://github.com/ci-monk/loli/commit/93f7465dfffe0afca875b72c0ea577393fcc177b)) 48 | * resize value ([43687e8](https://github.com/ci-monk/loli/commit/43687e83eaa859e39557b571678c3539a25f60cd)) 49 | 50 | 51 | ### :bug: Fix 52 | 53 | * remove root files ([6a6e93b](https://github.com/ci-monk/loli/commit/6a6e93b0907d29c912dcfc34daf6c9c088b056bc)) 54 | * upgrade golang to 1.19 ([b7f459f](https://github.com/ci-monk/loli/commit/b7f459f2700cbd7b028ca73c14cf234459bdc523)) 55 | 56 | 57 | ### :repeat: CI 58 | 59 | * change semantic release rules path ([c2691b8](https://github.com/ci-monk/loli/commit/c2691b8dc62ceac515c3ffef4ea9dc86e583fabd)) 60 | 61 | ## [1.14.1](https://github.com/ci-monk/loli/compare/v1.14.0...v1.14.1) (2022-09-27) 62 | 63 | 64 | ### :bug: Fix 65 | 66 | * brew cli name ([f7be5f0](https://github.com/ci-monk/loli/commit/f7be5f0833f53983973867d40fa0d1f6ae65f344)) 67 | * goreleaser setup ([01104c6](https://github.com/ci-monk/loli/commit/01104c6581e6df3471107a11f8d94650c3dc85d6)) 68 | 69 | ## [1.14.0](https://github.com/ci-monk/loli/compare/v1.13.1...v1.14.0) (2022-09-26) 70 | 71 | 72 | ### :sparkles: News 73 | 74 | * add check brewn upgrade ([13eac32](https://github.com/ci-monk/loli/commit/13eac3296a8903d32aba46df5ea55a0d562c2621)) 75 | 76 | 77 | ### :bug: Fix 78 | 79 | * **deps:** update module github.com/pterm/pterm to v0.12.47 ([e2e54c1](https://github.com/ci-monk/loli/commit/e2e54c11c56b1cf165c2f5c2c09f56724e9aa34d)) 80 | 81 | ## [1.13.1](https://github.com/ci-monk/loli/compare/v1.13.0...v1.13.1) (2022-09-24) 82 | 83 | 84 | ### :repeat: CI 85 | 86 | * add args ([d40e192](https://github.com/ci-monk/loli/commit/d40e1927e7cbb33080ad656f0d10473352fe52d7)) 87 | * gh tokens in semantic release ([a3047bd](https://github.com/ci-monk/loli/commit/a3047bd210eacc3677e4362f56c02a6f03e8120c)) 88 | 89 | ## [1.13.0](https://github.com/ci-monk/loli/compare/v1.12.7...v1.13.0) (2022-09-24) 90 | 91 | 92 | ### :memo: Docs 93 | 94 | * install with brew ([2d01ebe](https://github.com/ci-monk/loli/commit/2d01ebeaf53df9d81d58981765b167ac2d52a680)) 95 | 96 | 97 | ### :bug: Fix 98 | 99 | * dockerfile setup ([d178d78](https://github.com/ci-monk/loli/commit/d178d78c2ebaae76e280fae8e7e49d9e1679527c)) 100 | * gitignore file ([347eae0](https://github.com/ci-monk/loli/commit/347eae099a0b389c53a8faeead1b4467c733584a)) 101 | 102 | 103 | ### :sparkles: News 104 | 105 | * add desc and license in goreleaser brew ([a8d1bf8](https://github.com/ci-monk/loli/commit/a8d1bf856f70fdddd1fbd8b95f4ad4bae312dc5d)) 106 | * add goreleaser ([278ac11](https://github.com/ci-monk/loli/commit/278ac11b6be8c6b41f46487f75bcff354e90651f)) 107 | 108 | ## [1.12.7](https://github.com/ci-monk/loli/compare/v1.12.6...v1.12.7) (2022-09-24) 109 | 110 | 111 | ### :bug: Fix 112 | 113 | * package.json rules ([2cc76a1](https://github.com/ci-monk/loli/commit/2cc76a16dcdb06a7dabf116a00741996b002baae)) 114 | 115 | ## [1.12.6](https://github.com/ci-monk/loli/compare/v1.12.5...v1.12.6) (2022-09-24) 116 | 117 | 118 | ### :bug: Fix 119 | 120 | * gitignore file ([6672fcc](https://github.com/ci-monk/loli/commit/6672fcc8be108b94c1799ddccb47c4ae506bac29)) 121 | * home brew setup ([59c982e](https://github.com/ci-monk/loli/commit/59c982edf1600be746b54427463ca598f931a684)) 122 | * remove formula folder ([31303af](https://github.com/ci-monk/loli/commit/31303af5d8245a740f807f3bc5074c96f49c3ada)) 123 | 124 | ## [1.12.5](https://github.com/ci-monk/loli/compare/v1.12.4...v1.12.5) (2022-09-24) 125 | 126 | 127 | ### :bug: Fix 128 | 129 | * goreleaser config ([c6921c4](https://github.com/ci-monk/loli/commit/c6921c4f86643b42abde5fbf6af6cdf182829109)) 130 | 131 | ## [1.12.4](https://github.com/ci-monk/loli/compare/v1.12.3...v1.12.4) (2022-09-24) 132 | 133 | 134 | ### :bug: Fix 135 | 136 | * brew goreleaser ([9176aa7](https://github.com/ci-monk/loli/commit/9176aa7c36424bab8b8cd4ec4401b1e8205b85ef)) 137 | * brew tap name ([5a7213f](https://github.com/ci-monk/loli/commit/5a7213fd79cf6868dc1ef581c3a64d56605c187e)) 138 | 139 | 140 | ### :repeat: CI 141 | 142 | * remove template ([7c62069](https://github.com/ci-monk/loli/commit/7c62069ff1dd37f781280b8f4887ebd9d5414cba)) 143 | 144 | ## [1.12.3](https://github.com/ci-monk/loli/compare/v1.12.2...v1.12.3) (2022-09-24) 145 | 146 | 147 | ### :repeat: CI 148 | 149 | * **issue-#132:** add new workflow to gorelease ([4ced8c5](https://github.com/ci-monk/loli/commit/4ced8c58927eaf124af5eb4c46f161d93ee3de36)), closes [issue-#132](https://github.com/ci-monk/issue-/issues/132) 150 | * **issue-#132:** setup call job ([7b79559](https://github.com/ci-monk/loli/commit/7b795590dc53e79fabafb51dde2d8d541224b1dc)), closes [issue-#132](https://github.com/ci-monk/issue-/issues/132) 151 | * **issue-#132:** style call jobs ([83b316b](https://github.com/ci-monk/loli/commit/83b316bb46e1d246d03aae3588e13bf9e77f8d62)), closes [issue-#132](https://github.com/ci-monk/issue-/issues/132) 152 | 153 | ## [1.12.2](https://github.com/ci-monk/loli/compare/v1.12.1...v1.12.2) (2022-09-24) 154 | 155 | 156 | ### :repeat: CI 157 | 158 | * **issue-#132:** change script args go release ([fb64cd5](https://github.com/ci-monk/loli/commit/fb64cd5016c2f6fc37c8df3479c373d05fb893db)), closes [issue-#132](https://github.com/ci-monk/issue-/issues/132) 159 | 160 | ## [1.12.1](https://github.com/ci-monk/loli/compare/v1.12.0...v1.12.1) (2022-09-24) 161 | 162 | 163 | ### :repeat: CI 164 | 165 | * **issue-#132:** add upload assets ([c1018dd](https://github.com/ci-monk/loli/commit/c1018dd2ac791502f7d2e99beab4c084d7f01f8e)), closes [issue-#132](https://github.com/ci-monk/issue-/issues/132) 166 | 167 | ## [1.12.0](https://github.com/ci-monk/loli/compare/v1.11.3...v1.12.0) (2022-09-24) 168 | 169 | 170 | ### :sparkles: News 171 | 172 | * add brew setup in goreleaser ([e719594](https://github.com/ci-monk/loli/commit/e7195947ec19a78d1496643bbf06a0cad1345793)) 173 | 174 | ## [1.11.3](https://github.com/ci-monk/loli/compare/v1.11.2...v1.11.3) (2022-09-23) 175 | 176 | 177 | ### :bug: Fix 178 | 179 | * golang call version ([52736c0](https://github.com/ci-monk/loli/commit/52736c01bc39c83d2b9743278ace4285952c2f38)) 180 | 181 | ## [1.11.2](https://github.com/ci-monk/loli/compare/v1.11.1...v1.11.2) (2022-09-23) 182 | 183 | 184 | ### :repeat: CI 185 | 186 | * change config file hadolint call ([da8154e](https://github.com/ci-monk/loli/commit/da8154ebfcfda99820c1fac285de7867f1e048d1)) 187 | 188 | 189 | ### :bug: Fix 190 | 191 | * change cli is update message ([0d12dc6](https://github.com/ci-monk/loli/commit/0d12dc6974780975b906c50e7ab1082238e377db)) 192 | * constant, cmd and utils ([15258f8](https://github.com/ci-monk/loli/commit/15258f88c2d4d945e9f9aad470c0805e5c092dec)) 193 | * **deps:** update module github.com/briandowns/spinner to v1.19.0 ([224c6e0](https://github.com/ci-monk/loli/commit/224c6e002610381a0103ee6263c5ee84bf6e0995)) 194 | * **deps:** update module github.com/kyokomi/emoji/v2 to v2.2.10 ([17bf155](https://github.com/ci-monk/loli/commit/17bf15554b63ed070243d97bd1fb646af1dbbdec)) 195 | * **deps:** update module github.com/muesli/termenv to v0.13.0 ([2d96c7b](https://github.com/ci-monk/loli/commit/2d96c7b4fb9be12914e9ec8a2370a22c711539bc)) 196 | * **deps:** update module github.com/pterm/pterm to v0.12.42 ([05a4473](https://github.com/ci-monk/loli/commit/05a4473b6ee00fa9afabc176ad297f4a9a5b1628)) 197 | * **deps:** update module github.com/pterm/pterm to v0.12.46 ([9b4bea9](https://github.com/ci-monk/loli/commit/9b4bea98900a750cf2173984d783c4870781b287)) 198 | * **deps:** update module github.com/sirupsen/logrus to v1.9.0 ([f3ea3ec](https://github.com/ci-monk/loli/commit/f3ea3ec824a7f69d857de8ce5ccb078ee9e4d5ff)) 199 | * **deps:** update module github.com/spf13/cobra to v1.5.0 ([13dac17](https://github.com/ci-monk/loli/commit/13dac179d7783c5f3ed31efe8a08f08b20ab06ee)) 200 | * general configs ([ea19f4d](https://github.com/ci-monk/loli/commit/ea19f4df5434ed819ddae028f55b500c9ddec37b)) 201 | * rename const folder, change search args and others ([63d4b42](https://github.com/ci-monk/loli/commit/63d4b42aa1b47da7590cf5d0484054218f1eb18e)) 202 | 203 | ## [1.11.1](https://github.com/ci-monk/loli/compare/v1.11.0...v1.11.1) (2022-06-17) 204 | 205 | 206 | ### :memo: Docs 207 | 208 | * add new topcis ([3b8582c](https://github.com/ci-monk/loli/commit/3b8582cbabfca890bb525f13b9cefef659825557)) 209 | * readme setup ([9fe80ba](https://github.com/ci-monk/loli/commit/9fe80ba9b6a4ef58150286f6b2b9bdac8e4887a5)) 210 | * remove badged ([e41cc6f](https://github.com/ci-monk/loli/commit/e41cc6f6c3db99c59a837e64f6ebfe3140e7b964)) 211 | * size images ([2c8cbca](https://github.com/ci-monk/loli/commit/2c8cbcac79ec112a16b557d23dcb82e7c27ddad3)) 212 | 213 | 214 | ### :bug: Fix 215 | 216 | * call dependencies internal git repo name ([802fc0d](https://github.com/ci-monk/loli/commit/802fc0d5e9e5d934169b5fd676c2279c329376e2)) 217 | * capture signals ([f48cad4](https://github.com/ci-monk/loli/commit/f48cad48e06e587a027f0bf73e8f2fd45354032a)) 218 | * rename variable names ([3f816db](https://github.com/ci-monk/loli/commit/3f816db356a4a37c9929b323be5ec617e1fdda31)) 219 | * rules goreleaser ([f72d9b4](https://github.com/ci-monk/loli/commit/f72d9b47fa5e1abefeb10082a9cf3e87a844097c)) 220 | * semantic release rc rule ([d6175a0](https://github.com/ci-monk/loli/commit/d6175a01187d4ba4e674884a3d13c2836e5a04ac)) 221 | * style docker-compose ([68244ec](https://github.com/ci-monk/loli/commit/68244ecd0ef445c9c4e91f3587a4341ad5492d0c)) 222 | * type structure ([4fd1206](https://github.com/ci-monk/loli/commit/4fd1206c7ff16e65f782508081fdc76af406f592)) 223 | 224 | ## [1.11.0](https://github.com/ci-monk/loli/compare/v1.10.0...v1.11.0) (2022-06-17) 225 | 226 | 227 | ### :sparkles: News 228 | 229 | * add gitpod ([748bb57](https://github.com/ci-monk/loli/commit/748bb579230e4a3f07bfd13080179392129d5a1f)) 230 | 231 | 232 | ### :memo: Docs 233 | 234 | * change readme image size ([532507e](https://github.com/ci-monk/loli/commit/532507ea12ee5780dccd126c3a1d6445bfbb975c)) 235 | * fix gif size ([8c6ba68](https://github.com/ci-monk/loli/commit/8c6ba6877e5b308f98406595bf31ccdd8afdee8b)) 236 | 237 | 238 | ### :bug: Fix 239 | 240 | * api folder location ([8176f1b](https://github.com/ci-monk/loli/commit/8176f1bcf862c6b914e71f0e5493920a399ae3df)) 241 | * ci, vars and config ([c1ad82e](https://github.com/ci-monk/loli/commit/c1ad82eedc1c6d008d7539fdfc5d2ef839a1ba8e)) 242 | * conflicts pull ([cff5e54](https://github.com/ci-monk/loli/commit/cff5e54378bfabd534606d9acf951846c27ac82d)) 243 | * **deps:** update module github.com/kyokomi/emoji/v2 to v2.2.9 ([b0f7717](https://github.com/ci-monk/loli/commit/b0f771735c33f1186bf8d2c14f014ebc1a3eef62)) 244 | * **deps:** update module github.com/muesli/termenv to v0.12.0 ([b26966f](https://github.com/ci-monk/loli/commit/b26966fed6eac44a826da78efe5c6bf065f6c35c)) 245 | * **deps:** update module github.com/pterm/pterm to v0.12.41 ([831dee2](https://github.com/ci-monk/loli/commit/831dee28b2df29ac5726839cc9d474544a7ab612)) 246 | * **deps:** update module github.com/spf13/cobra to v1.4.0 ([ff9a0f9](https://github.com/ci-monk/loli/commit/ff9a0f9b6efd41d1a3fbcd39d8bda9474fec14c3)) 247 | * docker ignore file ([826b755](https://github.com/ci-monk/loli/commit/826b755aaacb53ea73bd0d17e796878b4c205e99)) 248 | * **dockerfile:** entrypoint and cmd ([daaf8fd](https://github.com/ci-monk/loli/commit/daaf8fdbdc593552a87f13fabc92d0a092315086)) 249 | * general changes ([e8d443d](https://github.com/ci-monk/loli/commit/e8d443d9f2f144c2670dcf13b43d5601d54c8106)) 250 | * go mod dependencies ([66629a0](https://github.com/ci-monk/loli/commit/66629a03057dde6a51c832f2de941f62416c1c6c)) 251 | * pre commit setup ([d66e9b3](https://github.com/ci-monk/loli/commit/d66e9b334fccf75ec731a71bd856941b273baab7)) 252 | * remove helpers internal and use only utils ([c766146](https://github.com/ci-monk/loli/commit/c766146361065f66db6d46cc23e07000da04e212)) 253 | * remove travis and golang ci ([43209c7](https://github.com/ci-monk/loli/commit/43209c726b28e239b7bbd03fa9d786067f278b1a)) 254 | * set config files ([47df44e](https://github.com/ci-monk/loli/commit/47df44e8999f72ce9398f8c63ccd96d786c4b74a)) 255 | * setup config files ([8ec5adc](https://github.com/ci-monk/loli/commit/8ec5adcc5f0d35b8b2977f99941fa4e3546cfecc)) 256 | * setup logrus formatter ([958943f](https://github.com/ci-monk/loli/commit/958943f0717b23ff1550cc87a54321b72a458d15)) 257 | * version style, rootCmd variable and others ([76152c7](https://github.com/ci-monk/loli/commit/76152c768ab4c021538eaa574ef045a93b43dee6)) 258 | 259 | 260 | ### :repeat: CI 261 | 262 | * remove template grettings and rename stage ([2f1a7f8](https://github.com/ci-monk/loli/commit/2f1a7f8343d42db35e42d4bb22ae4ee30412cdd9)) 263 | * rollback config hadolint ([a0c08b7](https://github.com/ci-monk/loli/commit/a0c08b76d1fef1731b7a49b525570dc8c0e03815)) 264 | * setup gitleaks job ([3f1d025](https://github.com/ci-monk/loli/commit/3f1d025ed516a69bf4f0859b323130b9e34b4a7f)) 265 | * setup hadolint ([b8d1c9b](https://github.com/ci-monk/loli/commit/b8d1c9b1919439a0881c7fa2383912c57a3363ca)) 266 | 267 | ## [1.10.0](https://github.com/lpmatos/loli/compare/v1.9.1...v1.10.0) (2022-01-03) 268 | 269 | 270 | ### :memo: Docs 271 | 272 | * add example trace response ([82576df](https://github.com/lpmatos/loli/commit/82576df7c957c5f7c3536e6f78fa7d7b14c77025)) 273 | 274 | 275 | ### :sparkles: News 276 | 277 | * add anime title struct ([dd145f4](https://github.com/lpmatos/loli/commit/dd145f4a8fafdcee8bb78a000bf5919e552c16d6)) 278 | * add is adult in output search file ([9ac8d89](https://github.com/lpmatos/loli/commit/9ac8d89591168ec5e1a6284e4f4a5cbf630904f0)) 279 | * colored output is adult ([fd53a20](https://github.com/lpmatos/loli/commit/fd53a204619468c783f197a658e30a03931c6785)) 280 | * search usage type, function and command ([864eabb](https://github.com/lpmatos/loli/commit/864eabb897ec1a53e6212d04ce600ddbfacbe3cf)) 281 | * show is adult in search link ([4ab38d9](https://github.com/lpmatos/loli/commit/4ab38d922462e8a7583fd5a007a55e0281bd90a6)) 282 | 283 | 284 | ### :bug: Fix 285 | 286 | * concat tmp file with project url constants ([85636b9](https://github.com/lpmatos/loli/commit/85636b97d2ef86ed9531af80e2578b975dcfa861)) 287 | * conditional anime is adult function ([919fcfe](https://github.com/lpmatos/loli/commit/919fcfe0182d22c5403f044d483f41a43c03c1f6)) 288 | * remove quotes ci template ([28df865](https://github.com/lpmatos/loli/commit/28df865d1869733fa1725f3e09a466f9e9906c88)) 289 | * tmp file name ([a4683a2](https://github.com/lpmatos/loli/commit/a4683a2d5eca0a53b75cf05ec6c9a11ebd3232b4)) 290 | 291 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 292 | 293 | 294 | ### :bug: Fix 295 | 296 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 297 | * env output steps ([84484fa](https://github.com/lpmatos/loli/commit/84484fa45f3e90e729ee4cc21a406de0d70022db)) 298 | * get last tag command ([427cd05](https://github.com/lpmatos/loli/commit/427cd052cee70f2c1aba7313ec5c09590c2a1924)) 299 | * go release name template ([9ca2cb5](https://github.com/lpmatos/loli/commit/9ca2cb5799493c77ee3306c8dded4d20326e43c6)) 300 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 301 | * merge conflicts ([5d23dd1](https://github.com/lpmatos/loli/commit/5d23dd1169f0ec5f5c943739ab040012cd03a157)) 302 | * merge conflicts ([ec7e9ec](https://github.com/lpmatos/loli/commit/ec7e9ec9974b49176734058bdce1c003d8ebb1b9)) 303 | * merge conflicts ([8fb0407](https://github.com/lpmatos/loli/commit/8fb04075eca49623326868a5e69f67afcd5032ef)) 304 | * remove debug ([8c9ba34](https://github.com/lpmatos/loli/commit/8c9ba34c8e3ef012c8265f60ca9b0313c716b89d)) 305 | * setup command get last tag ([f3d4f2d](https://github.com/lpmatos/loli/commit/f3d4f2dd35dccea332c76bad8827c4ac789e491e)) 306 | * source tag ([da93a7b](https://github.com/lpmatos/loli/commit/da93a7b6c3f7cec9db90c711a730b627004e24fe)) 307 | 308 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 309 | 310 | 311 | ### :bug: Fix 312 | 313 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 314 | * env output steps ([84484fa](https://github.com/lpmatos/loli/commit/84484fa45f3e90e729ee4cc21a406de0d70022db)) 315 | * get last tag command ([427cd05](https://github.com/lpmatos/loli/commit/427cd052cee70f2c1aba7313ec5c09590c2a1924)) 316 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 317 | * merge conflicts ([5d23dd1](https://github.com/lpmatos/loli/commit/5d23dd1169f0ec5f5c943739ab040012cd03a157)) 318 | * merge conflicts ([ec7e9ec](https://github.com/lpmatos/loli/commit/ec7e9ec9974b49176734058bdce1c003d8ebb1b9)) 319 | * merge conflicts ([8fb0407](https://github.com/lpmatos/loli/commit/8fb04075eca49623326868a5e69f67afcd5032ef)) 320 | * remove debug ([8c9ba34](https://github.com/lpmatos/loli/commit/8c9ba34c8e3ef012c8265f60ca9b0313c716b89d)) 321 | * setup command get last tag ([f3d4f2d](https://github.com/lpmatos/loli/commit/f3d4f2dd35dccea332c76bad8827c4ac789e491e)) 322 | * source tag ([da93a7b](https://github.com/lpmatos/loli/commit/da93a7b6c3f7cec9db90c711a730b627004e24fe)) 323 | 324 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 325 | 326 | 327 | ### :bug: Fix 328 | 329 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 330 | * env output steps ([84484fa](https://github.com/lpmatos/loli/commit/84484fa45f3e90e729ee4cc21a406de0d70022db)) 331 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 332 | * merge conflicts ([ec7e9ec](https://github.com/lpmatos/loli/commit/ec7e9ec9974b49176734058bdce1c003d8ebb1b9)) 333 | * merge conflicts ([8fb0407](https://github.com/lpmatos/loli/commit/8fb04075eca49623326868a5e69f67afcd5032ef)) 334 | * remove debug ([8c9ba34](https://github.com/lpmatos/loli/commit/8c9ba34c8e3ef012c8265f60ca9b0313c716b89d)) 335 | * setup command get last tag ([f3d4f2d](https://github.com/lpmatos/loli/commit/f3d4f2dd35dccea332c76bad8827c4ac789e491e)) 336 | * source tag ([da93a7b](https://github.com/lpmatos/loli/commit/da93a7b6c3f7cec9db90c711a730b627004e24fe)) 337 | 338 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 339 | 340 | 341 | ### :bug: Fix 342 | 343 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 344 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 345 | * merge conflicts ([ec7e9ec](https://github.com/lpmatos/loli/commit/ec7e9ec9974b49176734058bdce1c003d8ebb1b9)) 346 | * merge conflicts ([8fb0407](https://github.com/lpmatos/loli/commit/8fb04075eca49623326868a5e69f67afcd5032ef)) 347 | * setup command get last tag ([f3d4f2d](https://github.com/lpmatos/loli/commit/f3d4f2dd35dccea332c76bad8827c4ac789e491e)) 348 | * source tag ([da93a7b](https://github.com/lpmatos/loli/commit/da93a7b6c3f7cec9db90c711a730b627004e24fe)) 349 | 350 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 351 | 352 | 353 | ### :bug: Fix 354 | 355 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 356 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 357 | * merge conflicts ([8fb0407](https://github.com/lpmatos/loli/commit/8fb04075eca49623326868a5e69f67afcd5032ef)) 358 | * source tag ([da93a7b](https://github.com/lpmatos/loli/commit/da93a7b6c3f7cec9db90c711a730b627004e24fe)) 359 | 360 | ### [1.9.1](https://github.com/lpmatos/loli/compare/v1.9.0...v1.9.1) (2022-01-03) 361 | 362 | 363 | ### :bug: Fix 364 | 365 | * debug release vars ([bcd58d3](https://github.com/lpmatos/loli/commit/bcd58d342e021ff54af05ad5a2f14fded14cb59b)) 366 | * go releaser params and envs ([ce2ef08](https://github.com/lpmatos/loli/commit/ce2ef081cdf44ddeb471f6601d848f6ffb73e8a7)) 367 | 368 | ## [1.9.0](https://github.com/lpmatos/loli/compare/v1.8.0...v1.9.0) (2022-01-03) 369 | 370 | 371 | ### :sparkles: News 372 | 373 | * manual trigger ([e0f1e47](https://github.com/lpmatos/loli/commit/e0f1e4721cf282424c224944bdbbe8079458d4f6)) 374 | * rename template ([aa6220a](https://github.com/lpmatos/loli/commit/aa6220a848b6785dd5c04343115b1746ab211154)) 375 | * setup go releaser ([ea05f4c](https://github.com/lpmatos/loli/commit/ea05f4c0b2164a5cccb576487e80ab42a7a6d45f)) 376 | 377 | 378 | ### :package: Chore 379 | 380 | * **release:** version 1.0.0 - Jan 3, 2022, 5:34 PM ([6f4ee31](https://github.com/lpmatos/loli/commit/6f4ee31e9f08eee0e5ef3d3dd936fa0f53df813c)) 381 | * **release:** version 1.9.0 - Jan 3, 2022, 5:30 PM ([9484462](https://github.com/lpmatos/loli/commit/9484462d415d9d629df90bd917977cf094db21e7)) 382 | 383 | 384 | ### :bug: Fix 385 | 386 | * command list tags ([67d6449](https://github.com/lpmatos/loli/commit/67d644988ff70b6f02681c7fd4cd94e82d9fa261)) 387 | * go releaser quotes ([36363c4](https://github.com/lpmatos/loli/commit/36363c4781abd425710b18734f5db473f7b7bbf3)) 388 | * merge conflicts ([34991d5](https://github.com/lpmatos/loli/commit/34991d55f03094d27e87f4d243b8f336e1d92f12)) 389 | * merge conflicts ([1f17a51](https://github.com/lpmatos/loli/commit/1f17a51d3f778ce586a51178dd03031c1573873c)) 390 | * remove changelog ([fcbc86e](https://github.com/lpmatos/loli/commit/fcbc86e8ffd0a76c44f90336c764298c5763cce6)) 391 | * rollback format ([09f0d3e](https://github.com/lpmatos/loli/commit/09f0d3e819000bdcdfc32f73ed70a727fc999e59)) 392 | * semantic release trigger and rules ([e4bcb8e](https://github.com/lpmatos/loli/commit/e4bcb8e388b673ce6685ed47f0bc1878c1d65b7a)) 393 | * setup general configs ([6a1a751](https://github.com/lpmatos/loli/commit/6a1a75197e6d3622f710c16ca55de1a3e92de022)) 394 | * tag format release rc ([da2455c](https://github.com/lpmatos/loli/commit/da2455c1297664e2a8b92c16d2450d75242f6e97)) 395 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [◀ Voltar](README.md) 6 | 7 |
8 | 9 | contributing 10 | 11 | **Diretrizes para o processo de contribuição** 12 | 13 |
14 | 15 | Obrigado por considerar contribuir com este projeto! Seguir nossas diretrizes facilitará seu onboarding e tornará sua colaboração mais eficaz. 16 | 17 | # Sumário 18 | 19 |
20 | Expandir 21 | 22 | 23 | 24 | 25 | - [Práticas](#pr%C3%A1ticas) 26 | - [Geral](#geral) 27 | - [Comunicação](#comunica%C3%A7%C3%A3o) 28 | - [Setup](#setup) 29 | - [DevBox](#devbox) 30 | - [Direnv](#direnv) 31 | - [Task](#task) 32 | - [Commit Messages](#commit-messages) 33 | - [Type](#type) 34 | - [Scope](#scope) 35 | - [Description](#description) 36 | - [MR Process](#mr-process) 37 | - [Steps](#steps) 38 | - [Reviewing](#reviewing) 39 | - [Versioning Process](#versioning-process) 40 | 41 | 42 | 43 |

(back to top)

44 | 45 |
46 | 47 | # Práticas 48 | 49 | ## Geral 50 | 51 | - Sempre crie branchs descritivas, exemplo: `feature/nova-funcionalidade` ou `fix/corrige-bug`. 52 | - Evite commits muito grandes. Prefira commits pequenos e atômicos. 53 | - Não reinvente a roda. Se você pesquisou e viu que já existe uma solução bem estabelecida para o seu problema, use-a. Isso economiza tempo e recurso. 54 | 55 | ## Comunicação 56 | 57 | - Se você não conseguir continuar uma tarefa, informe imediatamente sua equipe. A comunicação rápida evita atrasos e permite que outras pessoas te ajudem a resolver os problemas com mais rapidez. 58 | - Minimize o uso de IA na comunicação diária com a equipe. Valorizamos interações reais e genuínas. 59 | - Seja objetivo na sua comunição quando precisa de ajuda (isso não significa ser rude). 60 | - A comunicação assíncrona é uma grande aliada para equipes remotas. Para mais detalhes, clique [aqui](https://nohello.net/en/). 61 | 62 |

(back to top)

63 | 64 | # Setup 65 | 66 | Para você começar a contribuir, é essencial configurar seu ambiente de trabalho local: 67 | 68 | - **Instalar ferramentas**: Certifique-se de ter instalado todas as ferramentas de linha de comando que o projeto requer. 69 | - **Configurar variáveis de ambiente**: Ajuste as variáveis de ambiente para garantir que seu sistema esteja preparado para rodar o projeto. 70 | - **Executar scripts de automação**: Rode os scripts fornecidos para configurar dependências, inicializar bancos de dados e outras tarefas automatizadas. 71 | 72 | > [!NOTE] 73 | > Lembre-se: cada projeto tem seu próprio contexto e necessidades! 74 | 75 | Pensando nisso, elaboramos as etapas abaixo para te guiar. 76 | 77 | ## DevBox 78 | 79 | O **DevBox** é uma ferramenta CLI que cria ambientes de desenvolvimento isolados e reproduzíveis, sem precisar usar containers Docker ou a linguagem Nix de forma nativa. 80 | 81 | > [!NOTE] 82 | > Use essa opção se você não quiser instalar muitas ferramentas CLI diretamente em seu ambiente de trabalho. 83 | 84 | Siga essas etapas para configurar seu ambiente: 85 | 86 | - Instale o [devbox](https://www.jetify.com/devbox/docs/installing_devbox/): 87 | 88 | ```bash 89 | curl -fsSL https://get.jetpack.io/devbox | bash 90 | ``` 91 | 92 | - Inicialize seu projeto: 93 | 94 | ```bash 95 | devbox init 96 | ``` 97 | 98 | - Adicione os pacotes que deseja (vai mudar de projeto para projeto). Ex: 99 | 100 | ```json 101 | { 102 | "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.10.7/.schema/devbox.schema.json", 103 | "packages": ["awscli2@latest", "kubectl@1.29.3", "kubernetes-helm@3.14.3"], 104 | "shell": { 105 | "init_hook": ["echo 'Welcome to devbox!' > /dev/null"], 106 | "scripts": { 107 | "test": ["echo \"Error: no test specified\" && exit 1"] 108 | } 109 | } 110 | } 111 | ``` 112 | 113 | - Execute o seguinte comando para inicializar o shell temporário: 114 | 115 | ```bash 116 | devbox shell 117 | ``` 118 | 119 | Com isso, podemos garantir que todos no projeto tenham as mesmas ferramentas nas mesmas versões, necessárias para o processo de desenvolvimento. 120 | 121 | > [!NOTE] 122 | > Se você precisar de mais detalhes sobre essa configuração, verifique o arquivo [devbox.json](devbox.json) do seu projeto. Caso não exista, crie ele seguindo o passo a passo descrito acima. 123 | 124 | ## Direnv 125 | 126 | Para você automatizar certas ações em seu terminal sempre que você for trabalhar nesse projeto, configure o **Direnv**. Essa ferramenta vai ajustar o seu shell conforme o seu diretório atual. Assim, sempre que você entrar na pasta do projeto, o **Direnv** fará algo, como: carregará as variáveis definidas no `.env` ou disparar o shell do **DevBox**. 127 | 128 | Siga essas etapas para configurar seu ambiente: 129 | 130 | - Acesse a documentação do [direnv](https://direnv.net/docs/installation.html) e siga as instruções para instalá-lo. 131 | 132 | - Após a instalação, crie um arquivo `.env` na raiz do seu projeto para armazenar as variáveis de ambiente utilizadas. 133 | 134 | - Crie o arquivo `.envrc` com o seguinte conteúdo: 135 | 136 | ```bash 137 | # Dotenv Support 138 | [[ ! -f .env ]] || dotenv .env 139 | 140 | # Devbox Support 141 | has devbox && eval "$(devbox generate direnv --print-envrc)" && exit 0 142 | ``` 143 | 144 | - A primeira vez que você criar ou modificar um arquivo `.envrc`, você precisará autorizá-lo com o comando: 145 | 146 | ```bash 147 | direnv allow 148 | ``` 149 | 150 | Seguindo essas etapas, quando você navegar para a pasta do seu projeto, as variáveis de ambiente serão carregadas automaticamente e o **DevBox** será inicializado. 151 | 152 | > [!NOTE] 153 | > Se você precisar de mais detalhes sobre esse configuração, verifique o arquivo [.envrc](.envrc) do seu projeto. 154 | 155 | ## Task 156 | 157 | A ferramenta **task** oferece uma maneira conveniente de definir e gerenciar tarefas específicas do projeto, facilitando a automatização de scripts comuns e simplificando os fluxos de trabalho de desenvolvimento. 158 | 159 | > [!NOTE] 160 | > É semelhante à ferramenta `make`, que é utilizada principalmente para automatizar tarefas. 161 | 162 | Siga essas etapas para configurar seu ambiente: 163 | 164 | - Certifique-se de que você instalou o comando `task` seguindo as etapas de configuração do **DevBox**. 165 | - Caso não tenha seguido, acesse a documentação do [task](https://taskfile.dev/installation/) e siga as instruções para instalá-lo. 166 | - Execute o comando `task` no diretório raiz do projeto para ver todos os comandos disponíveis. 167 | 168 | > [!NOTE] 169 | > Se você precisar de mais detalhes sobre cada tarefa definida, verifique o arquivo [Taskfile.yaml](Taskfile.yaml) do seu projeto. Caso não exista, crie ele seguindo o passo a passo descrito acima. 170 | 171 |

(back to top)

172 | 173 | # Commit Messages 174 | 175 | Nesse projeto, exigimos que **todos os commits** sigam um formato específico de mensagem, o [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). Com isso, conseguimos: 176 | 177 | - **Clareza e Consistência**: As mensagens de commit seguem um formato padrão, facilitando a leitura e a compreensão das mudanças. 178 | - **Automatização**: Permite a automação de processos, como geração de changelogs, versionamento semântico e lançamentos automáticos. 179 | - **Rastreamento de Alterações**: Facilita o rastreamento de mudanças ao longo do tempo, tornando mais fácil identificar o que foi modificado e por quê. 180 | - **Melhoria na Revisão de Código**: Proporciona uma melhor experiência de revisão de código, já que as mudanças são descritas de forma clara e padronizada. 181 | - **Comunicação Eficaz**: Ajuda a todos os membros da equipe a entenderem rapidamente o contexto e o propósito de cada alteração no código. 182 | 183 | Veja como é organizado esse formato de commits: 184 | 185 | ```txt 186 | (optional scope): 187 | 188 | [optional body] 189 | ``` 190 | 191 | ## Type 192 | 193 | Descreve o tipo de alteração do commit. Temos as seguintes opções: 194 | 195 | | Tipo | Descrição | 196 | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 197 | | **feat** | Um novo recurso (adição de um novo componente, fornecimento de novas variantes para um componente existente, etc.). | 198 | | **fix** | Uma correção de bug (correção de um problema de estilo, resolução de um bug na API de um componente etc.). Ao atualizar dependências que não sejam de desenvolvimento, marque suas alterações como `fix`. | 199 | | **docs** | Alterações somente na documentação. | 200 | | **style** | Alterações que não afetam o significado do código (espaços em branco, formatação, falta de ponto e vírgula etc.). Não deve ser usado para alterações na interface do usuário, pois essas são alterações significativas; em vez disso, considere usar `feat` ou `fix`. | 201 | | **refactor** | Uma alteração de código que não corrige um bug nem adiciona um recurso. | 202 | | **perf** | Uma alteração de código que melhora o desempenho. | 203 | | **test** | Adição de testes ausentes ou correção de testes existentes. | 204 | | **build** | Alterações que afetam o sistema de build. | 205 | | **ci** | Alterações em arquivos e scripts de configuração de CI/CD. | 206 | | **chore** | Outras alterações que não modificam arquivos de origem ou de teste. Use esse tipo ao adicionar ou atualizar dependências de desenvolvimento. | 207 | | **revert** | Reverte um commit anterior. | 208 | 209 | ## Scope 210 | 211 | É qualquer coisa que forneça informações adicionais ou que especifique o local de alteração do seu código. Por exemplo `events`, `kafka`, `dockerfile`, `authorization` e etc. Cada tipo (`type`) de commit pode ter um escopo (`scope`) opcional, cabendo a você adicionar ou omitir essa informação. Por exemplo: 212 | 213 | ``` 214 | feat(login): add route 215 | ``` 216 | 217 | > [!NOTE] 218 | > Use a convenção [PascalCase](https://www.dio.me/articles/camel-case-vs-pascal-case) na hora de definir seu escopo (`scope`). 219 | 220 | ## Description 221 | 222 | É o campo onde você diz o que foi feito no commit, porém de forma breve. Para isso, recomendamos que: 223 | 224 | - Priorize descrições em inglês. 225 | - Use o imperativo, tempo presente: "change", não "changed" ou "changing". 226 | - Não coloque a primeira letra em maiúscula. 227 | - Não coloque ponto (.) no final. 228 | 229 | > [!NOTE] 230 | > Cada tipo de commit tem um efeito sobre a próxima release que você for lançar. 231 | 232 | Existem mais opções, porém essas são as mais comuns. Para mais detalhes, consulte a [documentação oficial](https://www.conventionalcommits.org/en/v1.0.0/). 233 | 234 |

(back to top)

235 | 236 | # MR Process 237 | 238 | Ao criar um Merge Request (MR), recomenda-se definir o título conforme a mesma convenção adotada para mensagens de commit. Isso garante que, em caso de **squash merge**, o título do MR possa ser diretamente utilizado como a mensagem final do commit, preservando a padronização do histórico. Essa prática contribui para um log de alterações mais estruturado, conciso e linear, facilitando a rastreabilidade e automação de releases em fluxos de desenvolvimento que seguem padrões como **Conventional Commits**. 239 | 240 | ## Steps 241 | 242 | - Crie uma branch a partir da branch `main`: 243 | 244 | ```bash 245 | git checkout main 246 | git pull origin main 247 | git checkout -b sua-nova-branch 248 | ``` 249 | 250 | - Trabalhe na branch criada: 251 | - Realize as alterações necessárias e faça os commits das mudanças. 252 | - Certifique-se de que seu código atenda os padrões de qualidade estabelecidos. 253 | - Garanta que seus commits sigam a convenção de commits definida acima. 254 | 255 | ```bash 256 | git add . 257 | git commit -m "fix: change the commit" 258 | ``` 259 | 260 | - Faça o push da sua branch: 261 | 262 | ```bash 263 | git push origin sua-nova-branch 264 | ``` 265 | 266 | - Abra uma solicitação de MR: 267 | 268 | - Navegue até o repositório e abra uma nova Merge Request da sua branch para a branch de produção `main`. 269 | - Adicione uma descrição clara do que foi feito e qualquer informação relevante para o processo de review. 270 | - Defina o título usando commits convencionais. 271 | - Marque a opção de remover a branch de origem após a mesclagem. 272 | - Marque a opção para squash dos commits. 273 | 274 | - Revisão e Aprovação: 275 | 276 | - Um mantenedor revisará seu código. 277 | - Se o código atender aos requisitos e padrões, ele será aprovado. 278 | - Após a aprovação, seu código será mesclado na branch `main`. 279 | 280 | - Finalização: 281 | - Após a mesclagem, sua branch pode ser deletada se não for mais necessária. 282 | - Certifique-se de executar o `git pull` na branch `main`. 283 | 284 | Seguir este processo garante que as alterações sejam revisadas adequadamente e que o código de produção permaneça estável e com qualidade. 285 | 286 | > [!NOTE] 287 | > Se você tiver vários commits em seu PR, é recomendável usar a opção de squash para manter um histórico limpo e organizado. 288 | 289 | ## Reviewing 290 | 291 | Durante o processo de revisão do MR, siga essas políticas: 292 | 293 | - Seja respeitoso e construtivo. 294 | - Busque realizar um code review completo, verificando a lógica, a qualidade do código e a conformidade com os padrões estabelecidos. 295 | - Sugira alterações em vez de simplesmente comentar os problemas encontrados. 296 | - Exigimos pelo menos um aprovador no MR, que não seja o autor. 297 | - Se não tiver certeza sobre algo, pergunte ao autor do MR. 298 | - Se você estiver satisfeito com as alterações, aprove o MR. 299 | - Preze por pair programming, sempre que possível. 300 | 301 |

(back to top)

302 | 303 | # Versioning Process 304 | 305 | Esse projeto segue a especificação [SemVer](https://semver.org/). Ou seja, utilizamos um sistema de versionamento semântico para controlar as versões do projeto. Isso nos ajuda a comunicar melhor as mudanças feitas no código e a garantir a compatibilidade entre as versões. Esse sistema é composto por três números (`x.x.x`), que representam respectivamente: 306 | 307 | 1. **Major**: Quando fazemos alterações incompatíveis com a versão atual. Ex: `1.2.0 → 2.0.0` 308 | 2. **Minor**: Quando adicionamos funcionalidades de forma compatível com a versão atual. Ex: `1.2.0 → 1.3.0` 309 | 3. **Patch**: Quando corrigimos bugs de forma compatível com a versão atual. Ex: `1.2.0 → 1.2.1` 310 | 311 | Nós usamos o [Semantic Release](https://semantic-release.gitbook.io/semantic-release/) para automatizar esse processo de versionamento. Ele analisa os commits desde a última versão e determina automaticamente o próximo número da versão com base nas alterações feitas. Isso garante que as versões sejam consistentes e sigam as regras do SemVer. 312 | 313 | Vale ressaltar que o Semantic Release é acionado automaticamente quando um MR é mesclado na branch `main` ou quando existe um commit direto na branch `main`. Portanto, é importante seguir as convenções de commit para garantir que as versões sejam geradas corretamente. 314 | 315 | > [!NOTE] 316 | > O Semantic Release é um ferramental poderoso, e possui um sistema de plugins que podem ser configurados para atender as necessidades específicas do projeto. Para mais detalhes, consulte a [documentação oficial](https://semantic-release.gitbook.io/semantic-release/). 317 | 318 |

(back to top)

319 | 320 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20.4-alpine3.16 as builder 2 | WORKDIR /app 3 | COPY go.mod go.sum ./ 4 | RUN go mod download 5 | COPY . . 6 | RUN CGO_ENABLED=0 go build -o loli ./cmd/loli/main.go 7 | 8 | FROM alpine:3.21 9 | WORKDIR /app 10 | COPY --from=builder /app/loli ./ 11 | ENTRYPOINT [ "/app/loli" ] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lucca 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKEFLAGS += --warn-undefined-variables 2 | 3 | export CLIENT_VERSION = $(CLIENT_VERSION) 4 | export GO_VERSION = $(GO_VERSION) 5 | export GIT_BRANCH = $(GIT_BRANCH) 6 | 7 | # ================================================ 8 | # GENERIC VARIABLES 9 | # ================================================ 10 | 11 | BINDIR := $(CURDIR)/bin 12 | BINNAME ?= loli 13 | CLIENT_VERSION := $(shell git describe --tags --abbrev=0 ) 14 | BUILD_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') 15 | MAIN ?= $(CURDIR)/cmd/loli/main.go 16 | 17 | # ================================================ 18 | # GIT VARIABLES 19 | # ================================================ 20 | 21 | GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) 22 | GIT_COMMIT := $(shell git rev-parse HEAD) 23 | GIT_SHORT_COMMIT := $(shell git rev-parse --short HEAD) 24 | GIT_TAG := $(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi) 25 | GIT_TREE_STATE := $(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi) 26 | 27 | # ================================================ 28 | # GO VARIABLES 29 | # ================================================ 30 | 31 | GO_VERSION := $(shell go version) 32 | GOPATH ?= $(shell go env GOPATH) 33 | 34 | # Ensure GOPATH is set before running build process. 35 | ifeq "$(GOPATH)" "" 36 | $(error Please set the environment variable GOPATH before running `make`) 37 | endif 38 | 39 | GO := go 40 | GOOS := $(shell go env GOOS) 41 | GOARCH := $(shell go env GOARCH) 42 | ifeq (,$(shell go env GOBIN)) 43 | GOBIN=$(shell go env GOPATH)/bin 44 | else 45 | GOBIN=$(shell go env GOBIN) 46 | endif 47 | 48 | GOLANGCILINT_VERSION ?= v1.46.2 49 | 50 | # NOTE: '-race' requires cgo; enable cgo by setting CGO_ENABLED=1 51 | BUILD_FLAG := -race 52 | GOBUILD := CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build $(BUILD_FLAG) 53 | 54 | LDFLAGS := -w -s 55 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.cliVersion=$(CLIENT_VERSION)" 56 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.builtDate=$(BUILD_DATE)" 57 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.builtBy=makefile" 58 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.commit=$(GIT_COMMIT)" 59 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.commitShort=$(GIT_SHORT_COMMIT)" 60 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.commitBranch=$(GIT_BRANCH)" 61 | LDFLAGS += -X "github.com/lpmatos/loli/internal/version.goVersion=$(GO_VERSION)" 62 | 63 | ################################################## 64 | # HELPER 65 | ################################################## 66 | 67 | .PHONY: help 68 | help: 69 | @echo "Management commands for loli:" 70 | @echo "" 71 | @echo "Usage:" 72 | @echo "" 73 | @echo "** Golang Commands **" 74 | @echo "" 75 | @echo "make setup" 76 | @echo "make build" 77 | @echo "make install" 78 | @echo "make clean" 79 | @echo "make lint" 80 | @echo "make verify-goreleaser" 81 | @echo "make snapshot" 82 | @echo "make release" 83 | @echo "" 84 | 85 | 86 | 87 | ################################################## 88 | # GOLANG SHORTCUTS 89 | ################################################## 90 | 91 | .PHONY: setup 92 | setup: 93 | @echo "==> Setup..." 94 | $(GO) mod download 95 | $(GO) mod tidy 96 | $(GO) generate -v ./... 97 | @echo "" 98 | 99 | .PHONY: build 100 | build: 101 | @echo "==> Building..." 102 | $(GOBUILD) -o $(BINDIR)/$(BINNAME) -ldflags '$(LDFLAGS)' $(MAIN) 103 | @echo "" 104 | 105 | .PHONY: install 106 | install: 107 | @echo "==> Installing..." 108 | $(GO) install -x $(MAIN) 109 | @echo "" 110 | 111 | .PHONY: clean 112 | clean: 113 | @echo "==> Cleaning..." 114 | $(GO) clean -x -i $(MAIN) 115 | rm -rf ./bin/* ./vendor ./dist *.tar.gz 116 | @echo "" 117 | 118 | golangci: 119 | ifeq (, $(shell which golangci-lint)) 120 | @{ \ 121 | set -e ;\ 122 | echo 'installing golangci-lint-$(GOLANGCILINT_VERSION)' ;\ 123 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) $(GOLANGCILINT_VERSION) ;\ 124 | echo 'Install succeed' ;\ 125 | } 126 | GOLANGCILINT=$(GOBIN)/golangci-lint 127 | else 128 | GOLANGCILINT=$(shell which golangci-lint) 129 | endif 130 | 131 | .PHONY: lint 132 | lint: golangci 133 | $(GOLANGCILINT) run ./... 134 | 135 | .PHONY: verify-goreleaser 136 | verify-goreleaser: 137 | ifeq (, $(shell which goreleaser)) 138 | $(error "No goreleaser in $(PATH), consider installing it from https://goreleaser.com/install") 139 | endif 140 | goreleaser --version 141 | 142 | .PHONY: snapshot 143 | snapshot: verify-goreleaser 144 | goreleaser --snapshot --skip-publish --rm-dist 145 | 146 | .PHONY: release 147 | release: verify-goreleaser 148 | goreleaser release --rm-dist --debug 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | **Loli CLI** 7 | 8 | pipelines 9 | 10 | Hello Humans 👽! Loli is a pretty CLI that search animes passing images or links 11 | 12 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](https://www.conventionalcommits.org/en/v1.0.0/) [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://semantic-release.gitbook.io/semantic-release/usage/configuration) [![Built with Devbox](https://jetpack.io/img/devbox/shield_galaxy.svg)](https://jetpack.io/devbox/docs/contributor-quickstart/) 13 | 14 |
15 | 16 | # Visão Geral 17 | 18 | Este projeto é um **CLI** que busca animes a partir de imagens ou links. Ele utiliza a API do [trace.moe](https://soruly.github.io/trace.moe-api/#/) para realizar a busca. 19 | 20 |

(back to top)

21 | 22 | # Como Instalar? 23 | 24 | with `go`: 25 | 26 | ```bash 27 | go install github.com/ci-monk/loli/cmd/loli 28 | 29 | # if you cannot install directly, try following command, 30 | # then input install command again 31 | go get -x -u github.com/ci-monk/loli/cmd/loli 32 | ``` 33 | 34 | with `brew`: 35 | 36 | ```bash 37 | brew tap ci-monk/tools 38 | brew install loli 39 | ``` 40 | 41 | or use a binary from [releases](https://github.com/ci-monk/loli/releases/latest). 42 | 43 |

(back to top)

44 | 45 | # Usage 46 | 47 | ## Get anime with a file 48 | 49 | ```bash 50 | loli search file anime.jpg 51 | ``` 52 | 53 | ## Get anime with a link 54 | 55 | ```bash 56 | loli search link https://anime.com/image.png 57 | ``` 58 | 59 |

(back to top)

60 | 61 | # Trace.moe 62 | 63 | Example trace.moe response: 64 | 65 | ```json 66 | { 67 | "frameCount": 745506, 68 | "error": "", 69 | "result": [ 70 | { 71 | "anilist": { 72 | "id": 99939, 73 | "idMal": 34658, 74 | "title": { 75 | "native": "ネコぱらOVA", 76 | "romaji": "Nekopara OVA", 77 | "english": null 78 | }, 79 | "synonyms": ["Neko Para OVA"], 80 | "isAdult": false 81 | }, 82 | "filename": "Nekopara - OVA (BD 1280x720 x264 AAC).mp4", 83 | "episode": null, 84 | "from": 97.75, 85 | "to": 98.92, 86 | "similarity": 0.9440424588727485, 87 | "video": "https://media.trace.moe/video/99939/Nekopara%20-%20OVA%20(BD%201280x720%20x264%20AAC).mp4?t=98.33500000000001&token=xxxxxxxxxxxxxx", 88 | "image": "https://media.trace.moe/image/99939/Nekopara%20-%20OVA%20(BD%201280x720%20x264%20AAC).mp4?t=98.33500000000001&token=xxxxxxxxxxxxxx" 89 | } 90 | ] 91 | } 92 | ``` 93 | 94 |

(back to top)

95 | 96 | # Demo 97 | 98 | https://user-images.githubusercontent.com/58797390/192595643-a27003a5-d0ba-4abf-b8bb-19f449398190.mov 99 | 100 |

(back to top)

101 | 102 | # Referências 103 | 104 | Links relevantes para essa documentação: 105 | 106 | - https://soruly.github.io/trace.moe-api/#/ 107 | - https://img.olhardigital.com.br/wp-content/uploads/2021/07/Naruto-Classico-e-Naruto-Shippuden-fillers.jpg 108 | - https://images.plurk.com/32B15UXxymfSMwKGTObY5e.jpg 109 | 110 |

(back to top)

111 | 112 | # Versionamento 113 | 114 | Para verificar o histórico de mudanças, acesse o arquivo [**CHANGELOG.md**](CHANGELOG.md). 115 | 116 |

(back to top)

117 | 118 | # Troubleshooting 119 | 120 | Se você tiver algum problema ou queria contribuir, abra uma [issue](https://github.com/lpsm-dev/loli/issues/new/choose) nesse projeto. 121 | 122 |

(back to top)

123 | 124 | # Show your support 125 | 126 |
127 | 128 | Dê uma ⭐️ para este projeto se ele te ajudou! 129 | 130 | gif-footer 131 | 132 |
133 |
134 | 135 | Feito com 💜 pelo **Time de DevOps** :wave: inspirado no [readme-md-generator](https://github.com/kefranabg/readme-md-generator) 136 | 137 |
138 | 139 |

(back to top)

140 | 141 | -------------------------------------------------------------------------------- /Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | # Taskfile to be used with `task` binary. 4 | # Usage: 5 | # - Install with `asdf`: `asdf plugin add task` 6 | # - List available tasks with: `task --list` 7 | version: "3" 8 | 9 | vars: 10 | CLEAR: tput reset 11 | PATH_ERROR: is not installed or correctly configured in PATH. 12 | 13 | includes: 14 | precommit: .github/taskfiles/pre-commit.yaml 15 | 16 | tasks: 17 | default: 18 | silent: true 19 | aliases: [commands] 20 | cmds: 21 | - task --list 22 | 23 | clear: 24 | cmds: 25 | - sleep 0.1 && {{.CLEAR}} 26 | 27 | gitleaks: 28 | desc: Use the gitleaks tool to find secrets in the Git repository 29 | preconditions: 30 | - sh: which gitleaks 31 | msg: gitleaks {{.PATH_ERROR}} 32 | cmds: 33 | - gitleaks detect --source={{.ROOT_DIR}} --verbose 34 | 35 | yamllint: 36 | desc: Run the yamllint linter on all YAML files 37 | preconditions: 38 | - sh: which yamllint 39 | msg: yamllint {{.PATH_ERROR}} 40 | cmds: 41 | - yamllint --config-file={{.ROOT_DIR}}/.github/config/.yamllint.yaml . 42 | -------------------------------------------------------------------------------- /cmd/loli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import cmd "github.com/ci-monk/loli/commands" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /commands/cmd.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/ci-monk/loli/internal/utils" 8 | ) 9 | 10 | // Execute adds all child commands to the root command and sets flags appropriately. 11 | // This is called by loli main(). It only needs to happen once to the rootCmd. 12 | // 13 | // Args: 14 | // - rootCmd: The root command. 15 | func Execute() { 16 | // Show the header. 17 | utils.ShowHeader() 18 | 19 | // Execute the root command. 20 | if err := rootCmd.Execute(); err != nil { 21 | // Print the error message. 22 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 23 | // Exit with an error code. 24 | os.Exit(1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /commands/completion.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/ci-monk/loli/internal/consts" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | // excludeDesc is a flag that controls whether or not to include the shell completion 12 | // description in the generated script. 13 | var excludeDesc bool 14 | 15 | // completionCmd is the command that generates shell completion scripts. 16 | var completionCmd = &cobra.Command{ 17 | // Use is the command's name. 18 | Use: "completion ", 19 | // Short is a brief description of the command. 20 | Short: "Generate shell completion scripts", 21 | // Long is a longer description of the command. 22 | Long: consts.CompletionHelpMessage, 23 | // ValidArgs is a list of the valid shells that the command can generate completion scripts for. 24 | ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, 25 | // Args is a validation function that ensures that the user has only provided one argument, 26 | // and that the argument is one of the valid shells. 27 | Args: func(cmd *cobra.Command, args []string) error { 28 | if cobra.ExactArgs(1)(cmd, args) != nil || cobra.OnlyValidArgs(cmd, args) != nil { 29 | return fmt.Errorf("only %v arguments are allowed", cmd.ValidArgs) 30 | } 31 | return nil 32 | }, 33 | // RunE is the command's main function. It takes the command's arguments and 34 | // generates the shell completion script for the specified shell. 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | shellType := args[0] 37 | out, rootCmd := os.Stdout, cmd.Parent() 38 | switch shellType { 39 | case "bash": 40 | return rootCmd.GenBashCompletionV2(out, !excludeDesc) 41 | case "zsh": 42 | if excludeDesc { 43 | return rootCmd.GenZshCompletionNoDesc(out) 44 | } 45 | return rootCmd.GenZshCompletion(out) 46 | case "powershell": 47 | if excludeDesc { 48 | return rootCmd.GenPowerShellCompletion(out) 49 | } 50 | return rootCmd.GenPowerShellCompletionWithDesc(out) 51 | case "fish": 52 | return rootCmd.GenFishCompletion(out, !excludeDesc) 53 | default: 54 | return fmt.Errorf("unsupported shell type %q", shellType) 55 | } 56 | }, 57 | } 58 | 59 | // init registers the completionCmd command with the root command. 60 | func init() { 61 | // Flags are defined before the command is added to the root command. 62 | completionCmd.Flags().BoolVarP(&excludeDesc, "no-desc", "", false, "Do not include shell completion description") 63 | // Add the completionCmd command to the root command. 64 | rootCmd.AddCommand(completionCmd) 65 | } 66 | -------------------------------------------------------------------------------- /commands/root.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/consts" 5 | "github.com/ci-monk/loli/internal/debug" 6 | log "github.com/ci-monk/loli/internal/log" 7 | "github.com/ci-monk/loli/internal/utils" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var config = log.Config{} 12 | 13 | var rootCmd = &cobra.Command{ 14 | Use: consts.BinaryName, 15 | Short: "Find the anime scene by image using your terminal", 16 | Long: `Description: 17 | 18 | 👉😳👈 This is a pretty CLI that can find animes passing image scenes 19 | `, 20 | PersistentPreRun: func(cmd *cobra.Command, args []string) { 21 | config.SetDefault(config.Level, config.Format, config.Output, config.File, config.Verbose) 22 | 23 | err := log.Setup( 24 | log.WithConfig(config), 25 | ) 26 | 27 | if err != nil { 28 | log.Warn("Error setting log: %v", err) 29 | } 30 | 31 | if verbose, _ := cmd.Flags().GetBool("verbose"); verbose { 32 | debug.Verbose = verbose 33 | } 34 | 35 | if timeout, _ := cmd.Flags().GetInt("timeout"); timeout > 0 { 36 | consts.TimeoutInSeconds = timeout 37 | } 38 | }, 39 | } 40 | 41 | func init() { 42 | rootCmd.PersistentFlags().StringVar(&config.Level, "log-level", "debug", "set the logging level. One of: debug|info|warn|error") 43 | rootCmd.PersistentFlags().StringVar(&config.Format, "log-format", "color", "the formating of the logs. Available values: text|color|json|json-pretty") 44 | rootCmd.PersistentFlags().StringVar(&config.Output, "log-output", "stdout", "default log output. Available values: stdout|stderr|file") 45 | rootCmd.PersistentFlags().StringVar(&config.File, "log-file", utils.CreateLogFile("/var/log/loli", "file"), "defaulting Loli CLI log file") 46 | rootCmd.PersistentFlags().BoolVarP(&config.Verbose, "verbose", "v", false, "verbose output") 47 | rootCmd.PersistentFlags().IntP("timeout", "t", 0, "override the default HTTP timeout (seconds)") 48 | } 49 | -------------------------------------------------------------------------------- /commands/search.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | var searchPretty bool 8 | 9 | var searchCmd = &cobra.Command{ 10 | Use: "search", 11 | Aliases: []string{"find"}, 12 | Short: "Perform the search for an anime", 13 | Long: ``, 14 | } 15 | 16 | func init() { 17 | searchCmd.PersistentFlags().BoolVarP(&searchPretty, "pretty", "p", false, "Pretty output") 18 | rootCmd.AddCommand(searchCmd) 19 | } 20 | -------------------------------------------------------------------------------- /commands/search_file.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/trace" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var searchFileCmd = &cobra.Command{ 9 | Use: "file", 10 | Args: cobra.MinimumNArgs(1), 11 | Short: "Search for the anime scene by existing image file", 12 | Long: ``, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | trace.SearchAnimeByFile(args[0], searchPretty) 15 | }, 16 | } 17 | 18 | func init() { 19 | searchCmd.AddCommand(searchFileCmd) 20 | } 21 | -------------------------------------------------------------------------------- /commands/search_link.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/trace" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var searchLinkCmd = &cobra.Command{ 9 | Use: "link", 10 | Args: cobra.MinimumNArgs(1), 11 | Short: "Search for the anime scene by existing image link", 12 | Long: ``, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | trace.SearchAnimeByLink(args[0], searchPretty) 15 | }, 16 | } 17 | 18 | func init() { 19 | searchCmd.AddCommand(searchLinkCmd) 20 | } 21 | -------------------------------------------------------------------------------- /commands/search_usage.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/trace" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var searchMeCmd = &cobra.Command{ 9 | Use: "usage", 10 | Short: "Check the search quota and limit for your account (with API key) or IP address (without API key)", 11 | Long: ``, 12 | Run: func(cmd *cobra.Command, args []string) { 13 | trace.SearchUsage(searchPretty) 14 | }, 15 | } 16 | 17 | func init() { 18 | searchCmd.AddCommand(searchMeCmd) 19 | } 20 | -------------------------------------------------------------------------------- /commands/upgrade.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/ci-monk/loli/internal/cli" 10 | "github.com/ci-monk/loli/internal/version" 11 | "github.com/cli/safeexec" 12 | "github.com/kardianos/osext" 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var upgradeCmd = &cobra.Command{ 17 | Use: "upgrade", 18 | Aliases: []string{"u"}, 19 | Short: "Upgrade to the latest version of the CLI.", 20 | Long: `Upgrade to the latest version of the CLI. 21 | 22 | This finds and downloads the latest release, if you don't 23 | already have it. 24 | 25 | On Windows the old CLI will be left on disk, marked as hidden. 26 | The next time you upgrade, the hidden file will be overwritten. 27 | You can always delete this file. 28 | `, 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | loliBinary, _ := osext.Executable() 31 | isHomebrew := isUnderHomebrew(loliBinary) 32 | 33 | if isHomebrew { 34 | fmt.Printf("To upgrade, run: %s\n", "brew upgrade loli") 35 | return nil 36 | } else { 37 | c := cli.New(version.GetVersionFormatted()) 38 | err := updateCLI(c) 39 | if err != nil { 40 | return fmt.Errorf("we were not able to upgrade the cli because we encountered an error: %s", err) 41 | } 42 | } 43 | return nil 44 | }, 45 | } 46 | 47 | func updateCLI(c cli.Updater) error { 48 | ok, err := c.IsUpToDate() 49 | if err != nil { 50 | return err 51 | } 52 | 53 | if ok { 54 | fmt.Println("\n🎊 Your CLI is update 🎊") 55 | return nil 56 | } 57 | 58 | return c.Upgrade() 59 | } 60 | 61 | func isUnderHomebrew(binary string) bool { 62 | brewExe, err := safeexec.LookPath("brew") 63 | if err != nil { 64 | return false 65 | } 66 | 67 | brewPrefixBytes, err := exec.Command(brewExe, "--prefix").Output() 68 | if err != nil { 69 | return false 70 | } 71 | 72 | brewBinPrefix := filepath.Join(strings.TrimSpace(string(brewPrefixBytes)), "bin") + string(filepath.Separator) 73 | return strings.HasPrefix(binary, brewBinPrefix) 74 | } 75 | 76 | func init() { 77 | rootCmd.AddCommand(upgradeCmd) 78 | } 79 | -------------------------------------------------------------------------------- /commands/version.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/version" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var short, full bool 9 | 10 | var versionCmd = &cobra.Command{ 11 | Use: "version", 12 | Aliases: []string{"v"}, 13 | Short: "Version outputs the version of CLI", 14 | Long: `Version outputs the version of the loli binary that is in use`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | if short { 17 | version.GetShortDetails() 18 | } else { 19 | version.ShowVersion(full) 20 | } 21 | }, 22 | } 23 | 24 | func init() { 25 | versionCmd.PersistentFlags().BoolVarP(&short, "short", "s", false, "Show short details about the current version of loli CLI") 26 | versionCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Show full details about the current version of loli CLI") 27 | rootCmd.AddCommand(versionCmd) 28 | } 29 | -------------------------------------------------------------------------------- /devbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.0/.schema/devbox.schema.json", 3 | "packages": [ 4 | "direnv@2.35.0", 5 | "gitleaks@8.24.0", 6 | "golangci-lint@1.64.5", 7 | "pre-commit@4.0.1", 8 | "yamllint@1.35.1" 9 | ], 10 | "shell": { 11 | "init_hook": ["echo 'Welcome to devbox!' > /dev/null"], 12 | "scripts": { 13 | "test": ["echo \"Error: no test specified\" && exit 1"] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /devbox.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfile_version": "1", 3 | "packages": { 4 | "direnv@latest": { 5 | "last_modified": "2025-01-25T23:17:58Z", 6 | "resolved": "github:NixOS/nixpkgs/b582bb5b0d7af253b05d58314b85ab8ec46b8d19#direnv", 7 | "source": "devbox-search", 8 | "version": "2.35.0", 9 | "systems": { 10 | "aarch64-darwin": { 11 | "outputs": [ 12 | { 13 | "name": "out", 14 | "path": "/nix/store/q00mwz61l0kz3ikjafm8v42df79vw70m-direnv-2.35.0", 15 | "default": true 16 | } 17 | ], 18 | "store_path": "/nix/store/q00mwz61l0kz3ikjafm8v42df79vw70m-direnv-2.35.0" 19 | }, 20 | "aarch64-linux": { 21 | "outputs": [ 22 | { 23 | "name": "out", 24 | "path": "/nix/store/5qjaj63mcpkbgg9dd7kq326d3zv1nfsq-direnv-2.35.0", 25 | "default": true 26 | } 27 | ], 28 | "store_path": "/nix/store/5qjaj63mcpkbgg9dd7kq326d3zv1nfsq-direnv-2.35.0" 29 | }, 30 | "x86_64-darwin": { 31 | "outputs": [ 32 | { 33 | "name": "out", 34 | "path": "/nix/store/7w42jcbp3gl8mswmqkfpm54znbb58116-direnv-2.35.0", 35 | "default": true 36 | } 37 | ], 38 | "store_path": "/nix/store/7w42jcbp3gl8mswmqkfpm54znbb58116-direnv-2.35.0" 39 | }, 40 | "x86_64-linux": { 41 | "outputs": [ 42 | { 43 | "name": "out", 44 | "path": "/nix/store/5acgdhnifp5hsvhw2w34jhs5xqjgv0v3-direnv-2.35.0", 45 | "default": true 46 | } 47 | ], 48 | "store_path": "/nix/store/5acgdhnifp5hsvhw2w34jhs5xqjgv0v3-direnv-2.35.0" 49 | } 50 | } 51 | }, 52 | "gitleaks@latest": { 53 | "last_modified": "2025-01-25T23:17:58Z", 54 | "resolved": "github:NixOS/nixpkgs/b582bb5b0d7af253b05d58314b85ab8ec46b8d19#gitleaks", 55 | "source": "devbox-search", 56 | "version": "8.23.2", 57 | "systems": { 58 | "aarch64-darwin": { 59 | "outputs": [ 60 | { 61 | "name": "out", 62 | "path": "/nix/store/qrdnrrgbvzd48j6bbcizivsplh0yr092-gitleaks-8.23.2", 63 | "default": true 64 | } 65 | ], 66 | "store_path": "/nix/store/qrdnrrgbvzd48j6bbcizivsplh0yr092-gitleaks-8.23.2" 67 | }, 68 | "aarch64-linux": { 69 | "outputs": [ 70 | { 71 | "name": "out", 72 | "path": "/nix/store/5ms5b5kx669yw6s324p72qjb299iid3p-gitleaks-8.23.2", 73 | "default": true 74 | } 75 | ], 76 | "store_path": "/nix/store/5ms5b5kx669yw6s324p72qjb299iid3p-gitleaks-8.23.2" 77 | }, 78 | "x86_64-darwin": { 79 | "outputs": [ 80 | { 81 | "name": "out", 82 | "path": "/nix/store/wd48yd2w1nm193qr9ndgrcp8lf2271k9-gitleaks-8.23.2", 83 | "default": true 84 | } 85 | ], 86 | "store_path": "/nix/store/wd48yd2w1nm193qr9ndgrcp8lf2271k9-gitleaks-8.23.2" 87 | }, 88 | "x86_64-linux": { 89 | "outputs": [ 90 | { 91 | "name": "out", 92 | "path": "/nix/store/d38ns4b91jmaw19vi6081m4fgy4canjl-gitleaks-8.23.2", 93 | "default": true 94 | } 95 | ], 96 | "store_path": "/nix/store/d38ns4b91jmaw19vi6081m4fgy4canjl-gitleaks-8.23.2" 97 | } 98 | } 99 | }, 100 | "pre-commit@latest": { 101 | "last_modified": "2025-01-25T23:17:58Z", 102 | "resolved": "github:NixOS/nixpkgs/b582bb5b0d7af253b05d58314b85ab8ec46b8d19#pre-commit", 103 | "source": "devbox-search", 104 | "version": "4.0.1", 105 | "systems": { 106 | "aarch64-darwin": { 107 | "outputs": [ 108 | { 109 | "name": "out", 110 | "path": "/nix/store/9lz1d1szzwzy6x242kk9px91pkdy7cd6-pre-commit-4.0.1", 111 | "default": true 112 | }, 113 | { 114 | "name": "dist", 115 | "path": "/nix/store/hi34jjdsbbhq5xh1q0w47a23jpqf6iac-pre-commit-4.0.1-dist" 116 | } 117 | ], 118 | "store_path": "/nix/store/9lz1d1szzwzy6x242kk9px91pkdy7cd6-pre-commit-4.0.1" 119 | }, 120 | "aarch64-linux": { 121 | "outputs": [ 122 | { 123 | "name": "out", 124 | "path": "/nix/store/wnm7889axpdqqbl67n17kxw5sk31jjw5-pre-commit-4.0.1", 125 | "default": true 126 | }, 127 | { 128 | "name": "dist", 129 | "path": "/nix/store/qr97swbwvy1b3xp49xpqrv57vprqjwdq-pre-commit-4.0.1-dist" 130 | } 131 | ], 132 | "store_path": "/nix/store/wnm7889axpdqqbl67n17kxw5sk31jjw5-pre-commit-4.0.1" 133 | }, 134 | "x86_64-darwin": { 135 | "outputs": [ 136 | { 137 | "name": "out", 138 | "path": "/nix/store/w7fwcza4vnyxlsw2ikqjinqsn02ks06j-pre-commit-4.0.1", 139 | "default": true 140 | }, 141 | { 142 | "name": "dist", 143 | "path": "/nix/store/s7hxrwkn34q92g8pzw17v05ml3v10yys-pre-commit-4.0.1-dist" 144 | } 145 | ], 146 | "store_path": "/nix/store/w7fwcza4vnyxlsw2ikqjinqsn02ks06j-pre-commit-4.0.1" 147 | }, 148 | "x86_64-linux": { 149 | "outputs": [ 150 | { 151 | "name": "out", 152 | "path": "/nix/store/4x6p9yzvi0izpd3j4qj87z2d39lh0si6-pre-commit-4.0.1", 153 | "default": true 154 | }, 155 | { 156 | "name": "dist", 157 | "path": "/nix/store/m78p9134p8njvmi881zkfrskb7i7a9z7-pre-commit-4.0.1-dist" 158 | } 159 | ], 160 | "store_path": "/nix/store/4x6p9yzvi0izpd3j4qj87z2d39lh0si6-pre-commit-4.0.1" 161 | } 162 | } 163 | }, 164 | "yamllint@latest": { 165 | "last_modified": "2025-01-19T08:16:51Z", 166 | "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#yamllint", 167 | "source": "devbox-search", 168 | "version": "1.35.1", 169 | "systems": { 170 | "aarch64-darwin": { 171 | "outputs": [ 172 | { 173 | "name": "out", 174 | "path": "/nix/store/jlcrw184bxcbqcs6igjwal55425xxcm9-python3.12-yamllint-1.35.1", 175 | "default": true 176 | }, 177 | { 178 | "name": "dist", 179 | "path": "/nix/store/22vzn06fx5yzgfn80ka77fj0hy45nb23-python3.12-yamllint-1.35.1-dist" 180 | } 181 | ], 182 | "store_path": "/nix/store/jlcrw184bxcbqcs6igjwal55425xxcm9-python3.12-yamllint-1.35.1" 183 | }, 184 | "aarch64-linux": { 185 | "outputs": [ 186 | { 187 | "name": "out", 188 | "path": "/nix/store/6vzl6v6m6wrlv78xpw4bq38hidkcvscv-python3.12-yamllint-1.35.1", 189 | "default": true 190 | }, 191 | { 192 | "name": "dist", 193 | "path": "/nix/store/wqc08x7gsly5imchhifp079q8sgrpc5a-python3.12-yamllint-1.35.1-dist" 194 | } 195 | ], 196 | "store_path": "/nix/store/6vzl6v6m6wrlv78xpw4bq38hidkcvscv-python3.12-yamllint-1.35.1" 197 | }, 198 | "x86_64-darwin": { 199 | "outputs": [ 200 | { 201 | "name": "out", 202 | "path": "/nix/store/6idgrdfw4v396r10xj1wfj303ngxvq4d-python3.12-yamllint-1.35.1", 203 | "default": true 204 | }, 205 | { 206 | "name": "dist", 207 | "path": "/nix/store/6jlj9r9jazvd9hnp9m6l3p4r9d1bihgm-python3.12-yamllint-1.35.1-dist" 208 | } 209 | ], 210 | "store_path": "/nix/store/6idgrdfw4v396r10xj1wfj303ngxvq4d-python3.12-yamllint-1.35.1" 211 | }, 212 | "x86_64-linux": { 213 | "outputs": [ 214 | { 215 | "name": "out", 216 | "path": "/nix/store/cz1y92gzhb2ldxz8jhfmfg84r99v15z1-python3.12-yamllint-1.35.1", 217 | "default": true 218 | }, 219 | { 220 | "name": "dist", 221 | "path": "/nix/store/s04aw0g043zlpn6dwvfbgd8jaa22idgx-python3.12-yamllint-1.35.1-dist" 222 | } 223 | ], 224 | "store_path": "/nix/store/cz1y92gzhb2ldxz8jhfmfg84r99v15z1-python3.12-yamllint-1.35.1" 225 | } 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ci-monk/loli 2 | 3 | go 1.21 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/blang/semver v3.5.1+incompatible 9 | github.com/briandowns/spinner v1.23.2 10 | github.com/charmbracelet/glamour v0.8.0 11 | github.com/cli/safeexec v1.0.1 12 | github.com/fatih/color v1.18.0 13 | github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf 14 | github.com/jedib0t/go-pretty v4.3.0+incompatible 15 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 16 | github.com/kyokomi/emoji/v2 v2.2.13 17 | github.com/logrusorgru/aurora v2.0.3+incompatible 18 | github.com/logrusorgru/aurora/v3 v3.0.0 19 | github.com/muesli/termenv v0.16.0 20 | github.com/pterm/pterm v0.12.80 21 | github.com/sirupsen/logrus v1.9.3 22 | github.com/spf13/cobra v1.9.1 23 | ) 24 | 25 | require ( 26 | atomicgo.dev/cursor v0.2.0 // indirect 27 | atomicgo.dev/keyboard v0.2.9 // indirect 28 | atomicgo.dev/schedule v0.1.0 // indirect 29 | github.com/alecthomas/chroma v0.10.0 // indirect 30 | github.com/alecthomas/chroma/v2 v2.14.0 // indirect 31 | github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect 32 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 33 | github.com/aymerick/douceur v0.2.0 // indirect 34 | github.com/charmbracelet/lipgloss v0.12.1 // indirect 35 | github.com/charmbracelet/x/ansi v0.1.4 // indirect 36 | github.com/containerd/console v1.0.3 // indirect 37 | github.com/dlclark/regexp2 v1.11.0 // indirect 38 | github.com/go-openapi/errors v0.20.2 // indirect 39 | github.com/go-openapi/strfmt v0.21.2 // indirect 40 | github.com/go-stack/stack v1.8.1 // indirect 41 | github.com/gookit/color v1.5.4 // indirect 42 | github.com/gorilla/css v1.0.1 // indirect 43 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 44 | github.com/lithammer/fuzzysearch v1.1.8 // indirect 45 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 46 | github.com/mattn/go-colorable v0.1.13 // indirect 47 | github.com/mattn/go-isatty v0.0.20 // indirect 48 | github.com/mattn/go-runewidth v0.0.16 // indirect 49 | github.com/microcosm-cc/bluemonday v1.0.27 // indirect 50 | github.com/mitchellh/mapstructure v1.3.3 // indirect 51 | github.com/muesli/reflow v0.3.0 // indirect 52 | github.com/oklog/ulid v1.3.1 // indirect 53 | github.com/olekukonko/tablewriter v0.0.5 // indirect 54 | github.com/rivo/uniseg v0.4.7 // indirect 55 | github.com/spf13/pflag v1.0.6 // indirect 56 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 57 | github.com/yuin/goldmark v1.7.4 // indirect 58 | github.com/yuin/goldmark-emoji v1.0.3 // indirect 59 | go.mongodb.org/mongo-driver v1.8.3 // indirect 60 | golang.org/x/net v0.33.0 // indirect 61 | golang.org/x/sys v0.30.0 // indirect 62 | golang.org/x/term v0.27.0 // indirect 63 | golang.org/x/text v0.21.0 // indirect 64 | ) 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= 2 | atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= 3 | atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= 4 | atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= 5 | atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= 6 | atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= 7 | atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= 8 | atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= 9 | github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= 10 | github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= 11 | github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= 12 | github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= 13 | github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= 14 | github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= 15 | github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= 16 | github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= 17 | github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= 18 | github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= 19 | github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= 20 | github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= 21 | github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= 22 | github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 23 | github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= 24 | github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 25 | github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= 26 | github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 27 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 28 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 29 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 30 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 31 | github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= 32 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 33 | github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= 34 | github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= 35 | github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= 36 | github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= 37 | github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= 38 | github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= 39 | github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= 40 | github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= 41 | github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= 42 | github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= 43 | github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= 44 | github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= 45 | github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= 46 | github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= 47 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 48 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 49 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 50 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 52 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= 54 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 55 | github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 56 | github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 57 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 58 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 59 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 60 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 61 | github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= 62 | github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= 63 | github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= 64 | github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os= 65 | github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= 66 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 67 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= 68 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= 69 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 70 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 71 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 72 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 73 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 74 | github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= 75 | github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= 76 | github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 77 | github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= 78 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 79 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 80 | github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= 81 | github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= 82 | github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= 83 | github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= 84 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 85 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 86 | github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= 87 | github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= 88 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= 89 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 90 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 91 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 92 | github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 93 | github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 94 | github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= 95 | github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 96 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 97 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 98 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 99 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 100 | github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U= 101 | github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= 102 | github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= 103 | github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= 104 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 105 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 106 | github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= 107 | github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= 108 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 109 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 110 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 111 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 112 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 113 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= 114 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 115 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 116 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 117 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 118 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 119 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 120 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 121 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 122 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 123 | github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= 124 | github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= 125 | github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= 126 | github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= 127 | github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= 128 | github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 129 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 130 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 131 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 132 | github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= 133 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 134 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 135 | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= 136 | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= 137 | github.com/muesli/termenv v0.15.3-0.20241212154518-8c990cd6cf4b h1:gmVbquSG+bANneniKyO7R2DkOlCat7XDaSfXxHKKQBY= 138 | github.com/muesli/termenv v0.15.3-0.20241212154518-8c990cd6cf4b/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= 139 | github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 140 | github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 141 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 142 | github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= 143 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 144 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 145 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 146 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 147 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 148 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 149 | github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= 150 | github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= 151 | github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= 152 | github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= 153 | github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= 154 | github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= 155 | github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= 156 | github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg= 157 | github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo= 158 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 159 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 160 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 161 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 162 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 163 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 164 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 165 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 166 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 167 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 168 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 169 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 170 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 171 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 172 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 173 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 174 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 175 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 176 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 177 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 178 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 179 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 180 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 181 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 182 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 183 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= 184 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 185 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 186 | github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= 187 | github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= 188 | github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= 189 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 190 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 191 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 192 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 193 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 194 | github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= 195 | github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 196 | github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 197 | github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= 198 | github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 199 | github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= 200 | github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= 201 | github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= 202 | github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= 203 | go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= 204 | go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= 205 | go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= 206 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 207 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 208 | golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 209 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 210 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= 211 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= 212 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 213 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 214 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 215 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 216 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 217 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 218 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 219 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 220 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 221 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 222 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 223 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 224 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 225 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 226 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 227 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 228 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 229 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 230 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 231 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 232 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 233 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 234 | golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 235 | golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 236 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 237 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 238 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 239 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 240 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 241 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 242 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 243 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 244 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 245 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 246 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 247 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 248 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 249 | golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 250 | golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 251 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 252 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 253 | golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= 254 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 255 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 256 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 257 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 258 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 259 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 260 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 261 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 262 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 263 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 264 | golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 265 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 266 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 267 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 268 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 269 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 270 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 271 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 272 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 273 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 274 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 275 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 276 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 277 | gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 278 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 279 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 280 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 281 | -------------------------------------------------------------------------------- /internal/api/client.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "crypto/tls" 5 | "io" 6 | "net" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/ci-monk/loli/internal/consts" 11 | "github.com/ci-monk/loli/internal/debug" 12 | ) 13 | 14 | var ( 15 | // HTTPClient variable - is the client used to make HTTP calls in loli cli. 16 | // For more information: https://medium.com/@nate510/don-t-use-go-s-default-http-client-4804cb19f779 17 | HTTPClient = &http.Client{ 18 | Timeout: time.Duration(consts.TimeoutInSeconds) * time.Second, 19 | Transport: &http.Transport{ 20 | Proxy: http.ProxyFromEnvironment, 21 | DialContext: (&net.Dialer{ 22 | Timeout: 30 * time.Second, 23 | KeepAlive: 30 * time.Second, 24 | DualStack: true, 25 | }).DialContext, 26 | ForceAttemptHTTP2: true, 27 | MaxIdleConns: 100, 28 | IdleConnTimeout: 90 * time.Second, 29 | TLSHandshakeTimeout: 10 * time.Second, 30 | ExpectContinueTimeout: 1 * time.Second, 31 | TLSClientConfig: &tls.Config{ 32 | InsecureSkipVerify: consts.InsecureSkipVerify, 33 | }, 34 | }, 35 | } 36 | ) 37 | 38 | // NewClient returns a API client. 39 | func NewClient(baseURL string) (*Client, error) { 40 | return &Client{ 41 | Client: HTTPClient, 42 | BaseURL: baseURL, 43 | }, nil 44 | } 45 | 46 | // NewRequest returns an http.Request with information for the API. For more information: https://golang.org/pkg/net/http/ 47 | func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, error) { 48 | if c.Client == nil { 49 | c.Client = HTTPClient 50 | } 51 | 52 | req, error := http.NewRequest(method, url, body) 53 | if error != nil { 54 | return nil, error 55 | } 56 | 57 | req.Header.Set("User-Agent", consts.UserAgent) 58 | 59 | if c.ContentType == "" { 60 | req.Header.Set("Content-Type", "application/json") 61 | } else { 62 | req.Header.Set("Content-Type", c.ContentType) 63 | } 64 | 65 | return req, nil 66 | } 67 | 68 | // Do performs an http.Request and optionally parses the response body into the given interface. 69 | func (c *Client) Do(req *http.Request) (*http.Response, error) { 70 | debug.DumpRequest(req) 71 | 72 | res, err := c.Client.Do(req) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | debug.DumpResponse(res) 78 | return res, nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/api/types.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "net/http" 4 | 5 | // Client struct - is an http client that is configured for API. 6 | type Client struct { 7 | *http.Client // HTTP Client pointer 8 | BaseURL string // HTTP Request URL 9 | ContentType string // HTTP Request Content Type 10 | } 11 | -------------------------------------------------------------------------------- /internal/cli/asset.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | 9 | "github.com/ci-monk/loli/internal/consts" 10 | "github.com/ci-monk/loli/internal/log" 11 | ) 12 | 13 | func (a *Asset) download() (*bytes.Reader, error) { 14 | downloadReleaseURL := fmt.Sprintf("%s/assets/%d", consts.ReleaseURL, a.ID) 15 | log.Debug(downloadReleaseURL) 16 | 17 | request, err := http.NewRequest("GET", downloadReleaseURL, nil) 18 | if err != nil { 19 | log.Fatal(err) 20 | return nil, err 21 | } 22 | 23 | request.Header.Set("Accept", "application/octet-stream") 24 | 25 | log.Info("Doing the request to GitHub") 26 | resp, err := http.DefaultClient.Do(request) 27 | if err != nil { 28 | log.Fatal(err) 29 | return nil, err 30 | } 31 | defer resp.Body.Close() 32 | 33 | if resp.StatusCode != http.StatusOK { 34 | log.Fatalf("Bad status code - %d", resp.StatusCode) 35 | } 36 | 37 | content, err := io.ReadAll(resp.Body) 38 | if err != nil { 39 | log.Errorf("%s", err) 40 | return nil, err 41 | } 42 | 43 | return bytes.NewReader(content), nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/cli/cli.go: -------------------------------------------------------------------------------- 1 | //nolint:staticcheck // SA1019 ignore this! 2 | package cli 3 | 4 | import ( 5 | "archive/tar" 6 | "archive/zip" 7 | "bytes" 8 | "compress/gzip" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "runtime" 14 | "strings" 15 | 16 | "github.com/blang/semver" 17 | "github.com/ci-monk/loli/internal/api" 18 | "github.com/ci-monk/loli/internal/consts" 19 | "github.com/ci-monk/loli/internal/log" 20 | update "github.com/inconshreveable/go-update" 21 | ) 22 | 23 | var ( 24 | buildOS string 25 | buildARM string 26 | buildARCH string 27 | ) 28 | 29 | var ( 30 | osMap = map[string]string{ 31 | "darwin": "Darwin", 32 | "linux": "Linux", 33 | "windows": "Windows", 34 | } 35 | 36 | archMap = map[string]string{ 37 | "386": "i386", 38 | "amd64": "x86_64", 39 | "arm": "arm", 40 | } 41 | ) 42 | 43 | // New creates a CLI, setting it to a particular version. 44 | func New(version string) *CLI { 45 | return &CLI{ 46 | Version: version, 47 | } 48 | } 49 | 50 | // IsUpToDate compares the current version to that of the latest release. 51 | func (c *CLI) IsUpToDate() (bool, error) { 52 | log.Debug("Comparing the current version with the latest version") 53 | if c.LatestRelease == nil { 54 | if err := c.fetchLatestRelease(); err != nil { 55 | return false, err 56 | } 57 | } 58 | 59 | last := c.LatestRelease.Version() 60 | last = strings.Replace(last, "v", "", -1) 61 | 62 | log.Debugf("Latest Version - %s", last) 63 | 64 | rv, err := semver.Make(last) 65 | if err != nil { 66 | log.Error("Unable to parse latest version") 67 | return false, fmt.Errorf("unable to parse latest version (%s): %s", last, err) 68 | } 69 | 70 | current := strings.Replace(c.Version, "refs/tags/", "", -1) 71 | current = strings.Replace(current, "v", "", -1) 72 | log.Debugf("Current Version - %s", current) 73 | 74 | cv, err := semver.Make(current) 75 | if err != nil { 76 | log.Error("Unable to parse current version") 77 | return false, fmt.Errorf("unable to parse current version (%s): %s", current, err) 78 | } 79 | 80 | // GTE checks if v is greater than or equal to o. 81 | return cv.GTE(rv), nil 82 | } 83 | 84 | // Upgrade allows the user to upgrade to the latest version of the CLI. 85 | func (c *CLI) Upgrade() error { 86 | var ( 87 | OS = osMap[runtime.GOOS] 88 | ARCH = archMap[runtime.GOARCH] 89 | ) 90 | 91 | if OS == "" || ARCH == "" { 92 | log.Errorf("Unable to upgrade: OS %s ARCH %s", OS, ARCH) 93 | return fmt.Errorf("unable to upgrade: OS %s ARCH %s", OS, ARCH) 94 | } 95 | 96 | buildName := fmt.Sprintf("%s-%s", OS, ARCH) 97 | if buildARCH == "arm" { 98 | if buildARM == "" { 99 | log.Error("Unable to upgrade - ARM version not found") 100 | return fmt.Errorf("unable to upgrade - ARM version not found") 101 | } 102 | log.Debugf("Build ARCH version: %s - Build ARM version: %s", buildARCH, buildARM) 103 | buildName = fmt.Sprintf("%s-v%s", buildName, buildARM) 104 | } 105 | 106 | var downloadRC *bytes.Reader 107 | log.Info("Download latest release asset") 108 | for _, a := range c.LatestRelease.Assets { 109 | if strings.Contains(a.Name, buildName) { 110 | var err error 111 | downloadRC, err = a.download() 112 | if err != nil { 113 | log.Errorf("Error downloading executable: %s", err) 114 | return fmt.Errorf("error downloading executable: %s", err) 115 | } 116 | break 117 | } 118 | } 119 | 120 | if downloadRC == nil { 121 | log.Error("No executable found for") 122 | return fmt.Errorf("no executable found for %s/%s%s", buildOS, buildARCH, buildARM) 123 | } 124 | 125 | bin, err := extractBinary(downloadRC, OS) 126 | if err != nil { 127 | return err 128 | } 129 | defer bin.Close() 130 | 131 | return update.Apply(bin, update.Options{}) 132 | } 133 | 134 | func (c *CLI) fetchLatestRelease() error { 135 | log.Debug("Fetch latest release") 136 | 137 | latestReleaseURL := fmt.Sprintf("%s/%s", consts.ReleaseURL, "latest") 138 | resp, err := api.HTTPClient.Get(latestReleaseURL) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | defer resp.Body.Close() 144 | 145 | if resp.StatusCode > 399 { 146 | msg := "failed to get the latest release\n" 147 | for k, v := range resp.Header { 148 | msg += fmt.Sprintf("\n %s:\n %s", k, v) 149 | } 150 | return fmt.Errorf(msg) 151 | } 152 | 153 | var rel Release 154 | 155 | if err := json.NewDecoder(resp.Body).Decode(&rel); err != nil { 156 | return err 157 | } 158 | 159 | c.LatestRelease = &rel 160 | 161 | return nil 162 | } 163 | 164 | func extractBinary(source *bytes.Reader, os string) (binary io.ReadCloser, err error) { 165 | log.Info("Extract binary content") 166 | 167 | if os == "windows" { 168 | zr, err := zip.NewReader(source, int64(source.Len())) 169 | if err != nil { 170 | return nil, err 171 | } 172 | 173 | for _, f := range zr.File { 174 | info := f.FileInfo() 175 | if info.IsDir() || !strings.HasSuffix(f.Name, ".exe") { 176 | continue 177 | } 178 | return f.Open() 179 | } 180 | } else { 181 | gr, err := gzip.NewReader(source) 182 | if err != nil { 183 | return nil, err 184 | } 185 | defer gr.Close() 186 | 187 | tr := tar.NewReader(gr) 188 | for { 189 | _, err := tr.Next() 190 | if err == io.EOF { 191 | break 192 | } 193 | if err != nil { 194 | return nil, err 195 | } 196 | 197 | tmpfile, err := ioutil.TempFile("", fmt.Sprintf("temp-%s", consts.ProjectURL)) 198 | if err != nil { 199 | return nil, err 200 | } 201 | 202 | if _, err = io.Copy(tmpfile, tr); err != nil { 203 | return nil, err 204 | } 205 | if _, err := tmpfile.Seek(0, 0); err != nil { 206 | return nil, err 207 | } 208 | 209 | binary = tmpfile 210 | } 211 | } 212 | 213 | return binary, nil 214 | } 215 | -------------------------------------------------------------------------------- /internal/cli/release.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import "strings" 4 | 5 | // Version is the CLI version that is built for the release. 6 | func (r *Release) Version() string { 7 | return strings.TrimPrefix(r.TagName, "v") 8 | } 9 | -------------------------------------------------------------------------------- /internal/cli/types.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // Asset struct is a build for a particular loli version uploaded to GitHub releases. 4 | // For more information: https://docs.github.com/en/rest/reference/repos#releases 5 | // For more information: https://golang.org/doc/effective_go.html#names 6 | // For more information: https://medium.com/better-programming/naming-conventions-in-go-short-but-descriptive-1fa7c6d2f32a 7 | type Asset struct { 8 | ID int `json:"id"` 9 | Name string `json:"name"` 10 | ContentType string `json:"content_type"` 11 | } 12 | 13 | // Release is a specific build of the CLI, released on GitHub. 14 | type Release struct { 15 | Location string `json:"html_url"` 16 | TagName string `json:"tag_name"` 17 | Assets []Asset `json:"assets"` 18 | } 19 | 20 | // CLI is information about the CLI itself. 21 | type CLI struct { 22 | Version string 23 | LatestRelease *Release 24 | } 25 | 26 | // Updater is a simple upgradable file interface. 27 | type Updater interface { 28 | IsUpToDate() (bool, error) 29 | Upgrade() error 30 | } 31 | -------------------------------------------------------------------------------- /internal/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | // DefaultTimestampFormat default time format. 5 | DefaultTimestampFormat = "2006-01-02_15:04:05" 6 | 7 | // TraceMoeSearchAnimeByFile default trace.moe URL for file 8 | TraceMoeSearchAnimeByFile = "https://api.trace.moe/search?anilistInfo" 9 | 10 | // TraceMoeSearchAnimeByLink default trace.moe URL for link 11 | TraceMoeSearchAnimeByLink = "https://api.trace.moe/search?anilistInfo&url=" 12 | 13 | // TraceMoeUsage default trace.moe URL 14 | TraceMoeUsage = "https://api.trace.moe/me" 15 | 16 | // ReleaseURL default release URL. 17 | ReleaseURL = "https://api.github.com/repos/lpmatos/loli/releases" 18 | 19 | // UserAgent variable - lets the API know where the call is being made from. 20 | // For more information: https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Headers/User-Agent 21 | UserAgent = "github.com/lpmatos/loli" 22 | 23 | // BinaryName is the name of the app. 24 | BinaryName = "loli" 25 | 26 | // ProjectURL is the project url of the app. 27 | ProjectURL = "loli" 28 | 29 | // InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name. 30 | // For more information: https://golang.org/pkg/crypto/tls/ 31 | InsecureSkipVerify = false 32 | ) 33 | 34 | // Welcome - return a markdown welcome message. 35 | const Welcome = ` 36 | Hello there, fellow coders 🤖! 37 | 38 | If you want access this repository, copy this [link](https://github.com/lpmatos/loli). 👋 39 | ` 40 | 41 | // CompletionHelpMessage - return the long description of completion command. 42 | const CompletionHelpMessage = `To load completion for: 43 | 44 | Bash: 45 | - For bash, ensure you have bash completions installed and enabled. 46 | - To access completions in your current shell, run. 47 | - Alternatively, write it to a file and source in .bash_profile. 48 | $ source <(loli completion bash) 49 | 50 | Zsh: 51 | - For zsh, output to a file in a directory referenced by the $fpath shell. 52 | $ source <(loli completion zsh) 53 | 54 | # To load completions for each session, execute once: 55 | $ loli completion zsh > "${fpath[1]}/_loli" 56 | 57 | Fish: 58 | $ loli completion fish | source 59 | 60 | # To load completions for each session, execute once: 61 | $ loli completion fish > ~/.config/fish/completions/loli.fish 62 | ` 63 | 64 | var ( 65 | // TimeoutInSeconds variable - is the timeout the default HTTP client will use. 66 | // For more information: https://stackoverflow.com/questions/16895294/how-to-set-timeout-for-http-get-requests-in-golang 67 | TimeoutInSeconds = 60 68 | ) 69 | -------------------------------------------------------------------------------- /internal/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/http/httputil" 9 | "os" 10 | 11 | log "github.com/ci-monk/loli/internal/log" 12 | ) 13 | 14 | var ( 15 | // Verbose determines if debugging output is displayed to the user 16 | Verbose bool 17 | output io.Writer = os.Stderr 18 | ) 19 | 20 | // Println conditionally outputs a message to Stderr 21 | func Println(args ...interface{}) { 22 | if Verbose { 23 | fmt.Fprintln(output, args...) 24 | } 25 | } 26 | 27 | // DumpRequest dumps out the provided http.Request 28 | func DumpRequest(req *http.Request) { 29 | if !Verbose { 30 | return 31 | } 32 | 33 | var bodyCopy bytes.Buffer 34 | body := io.TeeReader(req.Body, &bodyCopy) 35 | req.Body = io.NopCloser(body) 36 | 37 | dump, err := httputil.DumpRequest(req, req.ContentLength > 0) 38 | if err != nil { 39 | log.Error(err) 40 | } 41 | 42 | Println("\n========================= BEGIN DumpRequest =========================") 43 | Println(string(dump)) 44 | Println("========================= END DumpRequest =========================") 45 | Println("") 46 | 47 | req.Body = io.NopCloser(&bodyCopy) 48 | } 49 | 50 | // DumpResponse dumps out the provided http.Response 51 | func DumpResponse(res *http.Response) { 52 | if !Verbose { 53 | return 54 | } 55 | 56 | var bodyCopy bytes.Buffer 57 | body := io.TeeReader(res.Body, &bodyCopy) 58 | res.Body = io.NopCloser(body) 59 | 60 | dump, err := httputil.DumpResponse(res, res.ContentLength > 0) 61 | if err != nil { 62 | log.Error(err) 63 | } 64 | 65 | Println("\n========================= BEGIN DumpResponse =========================") 66 | Println(string(dump)) 67 | Println("========================= END DumpResponse =========================") 68 | Println("") 69 | 70 | res.Body = io.NopCloser(body) 71 | } 72 | -------------------------------------------------------------------------------- /internal/log/logger.go: -------------------------------------------------------------------------------- 1 | //nolint:errcheck 2 | package log 3 | 4 | func applyOptions(opts []Option) *options { 5 | var o options 6 | o.logger = logger 7 | for _, opt := range opts { 8 | opt(&o) 9 | } 10 | return &o 11 | } 12 | 13 | // Setup returns a new logrus instance 14 | func Setup(opts ...Option) error { 15 | conf := applyOptions(opts) 16 | conf.logger.SetFormatter(conf.logger.Formatter) 17 | conf.logger.SetLevel(conf.logger.Level) 18 | return nil 19 | } 20 | 21 | func init() { 22 | logger = newLogger() 23 | logger.SetReportCaller(false) 24 | } 25 | -------------------------------------------------------------------------------- /internal/log/logger_config.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | // Config defines all the configurable options for the logrus logger 4 | type Config struct { 5 | Level string 6 | Format string 7 | Output string 8 | File string 9 | Verbose bool 10 | } 11 | 12 | // SetDefault set default values for logrus logger configurable options 13 | func (config *Config) SetDefault(level, formater, output, file string, verbose bool) { 14 | config.Level = level 15 | config.Format = formater 16 | config.Output = output 17 | config.File = file 18 | config.Verbose = verbose 19 | } 20 | -------------------------------------------------------------------------------- /internal/log/logger_formatters.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/ci-monk/loli/internal/consts" 5 | "github.com/sirupsen/logrus" 6 | ) 7 | 8 | // Configure the logrus format to use "text" formatter 9 | func textFormatter() *logrus.TextFormatter { 10 | return &logrus.TextFormatter{ 11 | DisableColors: true, 12 | ForceColors: true, 13 | EnvironmentOverrideColors: true, 14 | FullTimestamp: true, 15 | TimestampFormat: consts.DefaultTimestampFormat, 16 | DisableLevelTruncation: true, 17 | } 18 | } 19 | 20 | // Configure the logrus format to use "color" formatter 21 | func colorFormatter() *logrus.TextFormatter { 22 | return &logrus.TextFormatter{ 23 | DisableColors: false, 24 | ForceColors: true, 25 | EnvironmentOverrideColors: true, 26 | FullTimestamp: true, 27 | TimestampFormat: consts.DefaultTimestampFormat, 28 | DisableLevelTruncation: true, 29 | } 30 | } 31 | 32 | // Configure the logrus format to use "json" formatter 33 | func jsonFormatter(pretty bool) *logrus.JSONFormatter { 34 | return &logrus.JSONFormatter{ 35 | TimestampFormat: consts.DefaultTimestampFormat, 36 | DisableTimestamp: false, 37 | PrettyPrint: pretty, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/log/logger_messages.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kyokomi/emoji/v2" 7 | au "github.com/logrusorgru/aurora/v3" 8 | ) 9 | 10 | // Debug function to print a pretty formatted log debug message 11 | func Debug(args ...interface{}) { 12 | logger.Debug( 13 | au.BrightCyan( 14 | emoji.Sprintf("🆗 " + fmt.Sprintf("%v", args...)), 15 | ).Bold().Underline(), 16 | ) 17 | } 18 | 19 | // Debugf function to print a pretty formatted log debug message 20 | func Debugf(format string, args ...interface{}) { 21 | logger.Debugf( 22 | "🆗 "+au.BrightCyan(format). 23 | Bold(). 24 | Underline(). 25 | String(), args..., 26 | ) 27 | } 28 | 29 | // Debugln function to print a pretty formatted log debug message 30 | func Debugln(args ...interface{}) { 31 | logger.Debugln( 32 | au.BrightCyan( 33 | emoji.Sprintf("🆗 " + fmt.Sprintf("%v\n", args...)), 34 | ).Bold().Underline(), 35 | ) 36 | } 37 | 38 | // Info function to print a pretty formatted log info message 39 | func Info(args ...interface{}) { 40 | logger.Info( 41 | au.Green( 42 | emoji.Sprintf("✅ " + fmt.Sprintf("%v", args...)), 43 | ).Bold().Underline(), 44 | ) 45 | } 46 | 47 | // Infof function to print a pretty formatted log info message 48 | func Infof(format string, args ...interface{}) { 49 | logger.Infof( 50 | "✅ "+au.Green(format). 51 | Bold(). 52 | Underline(). 53 | String(), args..., 54 | ) 55 | } 56 | 57 | // Infoln function to print a pretty formatted log info message 58 | func Infoln(args ...interface{}) { 59 | logger.Infoln( 60 | au.Green( 61 | emoji.Sprintf("✅ " + fmt.Sprintf("%v\n", args...)), 62 | ).Bold().Underline(), 63 | ) 64 | } 65 | 66 | // Warn function to print a pretty formatted log warn message 67 | func Warn(args ...interface{}) { 68 | logger.Warn( 69 | au.BrightYellow( 70 | emoji.Sprintf("😲 " + fmt.Sprintf("%v", args...)), 71 | ).Bold().Underline(), 72 | ) 73 | } 74 | 75 | // Warnf function to print a pretty formatted log warn message 76 | func Warnf(format string, args ...interface{}) { 77 | logger.Warnf( 78 | "😲 "+au.BrightYellow(format). 79 | Bold(). 80 | Underline(). 81 | String(), args..., 82 | ) 83 | } 84 | 85 | // Warnln function to print a pretty formatted log warn message 86 | func Warnln(args ...interface{}) { 87 | logger.Warnln( 88 | au.BrightYellow( 89 | emoji.Sprintf("😲 " + fmt.Sprintf("%v\n", args...)), 90 | ).Bold().Underline(), 91 | ) 92 | } 93 | 94 | // Error function to print a pretty formatted log error message 95 | func Error(args ...interface{}) { 96 | logger.Error( 97 | au.BrightRed( 98 | emoji.Sprintf("😡 " + fmt.Sprintf("%v", args...)), 99 | )) 100 | } 101 | 102 | // Errorf function to print a pretty formatted log error message 103 | func Errorf(format string, args ...interface{}) { 104 | logger.Errorf( 105 | "😡 "+au.BrightRed(format). 106 | Bold(). 107 | Underline(). 108 | String(), args..., 109 | ) 110 | } 111 | 112 | // Errorln function to print a pretty formatted log error message 113 | func Errorln(args ...interface{}) { 114 | logger.Errorln( 115 | au.BrightRed( 116 | emoji.Sprintf("😡 " + fmt.Sprintf("%v\n", args...)), 117 | ).Bold().Underline(), 118 | ) 119 | } 120 | 121 | // Fatal function to print a pretty formatted log fatal message 122 | func Fatal(args ...interface{}) { 123 | logger.Fatal( 124 | au.BrightRed( 125 | emoji.Sprintf("🤬 " + fmt.Sprintf("%v", args...)), 126 | )) 127 | } 128 | 129 | // Fatalf function to print a pretty formatted log fatal message 130 | func Fatalf(format string, args ...interface{}) { 131 | logger.Fatalf( 132 | "🤬 "+au.BrightRed(format). 133 | Bold(). 134 | Underline(). 135 | String(), args..., 136 | ) 137 | } 138 | 139 | // Fatalln function to print a pretty formatted log fatal message 140 | func Fatalln(args ...interface{}) { 141 | logger.Fatalln( 142 | au.BrightRed( 143 | emoji.Sprintf("🤬 " + fmt.Sprintf("%v\n", args...)), 144 | ).Bold().Underline(), 145 | ) 146 | } 147 | -------------------------------------------------------------------------------- /internal/log/logger_options.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "runtime" 8 | "strings" 9 | 10 | "github.com/ci-monk/loli/internal/utils" 11 | "github.com/sirupsen/logrus" 12 | ) 13 | 14 | type options struct { 15 | logger *logrus.Logger 16 | } 17 | 18 | // Option will configure a new logrus Logger 19 | type Option func(*options) error 20 | 21 | // WithConfig takes the logger configuration and applies it 22 | func WithConfig(cfg Config) Option { 23 | var opts []Option 24 | 25 | opts = append(opts, WithLogLevel(cfg.Level, cfg.Verbose)) 26 | opts = append(opts, WithFormatter(cfg.Format)) 27 | opts = append(opts, WithOutputStr(cfg.Output, cfg.File)) 28 | 29 | // return all options 30 | return func(c *options) error { 31 | for _, opt := range opts { 32 | if err := opt(c); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | } 39 | 40 | // WithFormatter allows setting the format to `text`, `color`, `json`, `json-pretty` or `plain`. In case 41 | // the input is not recognized it defaults to text with a warning. 42 | // More details of these formats: 43 | // * `text` - human readable. 44 | // * `color` - human readable, in color. Useful for development. 45 | // * `json` - computer readable, new-line delimited JSON. 46 | // * `json-pretty` - computer readable pretty JSON. 47 | // * `plain` - custom human readable. 48 | func WithFormatter(format string) Option { 49 | return func(o *options) error { 50 | switch strings.ToLower(format) { 51 | case "text": 52 | o.logger.Formatter = textFormatter() 53 | case "color": 54 | o.logger.Formatter = colorFormatter() 55 | case "json": 56 | o.logger.Formatter = jsonFormatter(false) 57 | case "json-pretty": 58 | o.logger.Formatter = jsonFormatter(true) 59 | default: 60 | o.logger.Formatter = colorFormatter() 61 | } 62 | return nil 63 | } 64 | } 65 | 66 | // WithLogLevel is used to set the log level when defaulting to `info` is not 67 | // wanted. Other options are: `debug`, `info`, `warn` and `error` 68 | func WithLogLevel(level string, verbose bool) Option { 69 | return func(opt *options) error { 70 | logrusLevel, err := logrus.ParseLevel(level) 71 | if err != nil { 72 | return fmt.Errorf("failed to convert level: %w", err) 73 | } 74 | if verbose { 75 | opt.logger.Level = logrusLevel 76 | } else { 77 | opt.logger.Level = logrus.ErrorLevel 78 | } 79 | return nil 80 | } 81 | } 82 | 83 | // WithOutputStr allows customization of the sink of the logger. Output is either: 84 | // `stdout`, `stderr`, or `file` 85 | func WithOutputStr(output, file string) Option { 86 | opt := func(*options) error { return nil } 87 | 88 | if output == "" { 89 | return opt 90 | } 91 | 92 | switch strings.ToLower(output) { 93 | case "stdout": 94 | opt = WithOutput(os.Stdout) 95 | case "stderr": 96 | opt = WithOutput(os.Stdout) 97 | case "file": 98 | utils.MakeDirIfNotExist(file) 99 | 100 | f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 101 | if err != nil { 102 | return func(c *options) error { 103 | return fmt.Errorf("unable to open/create file %q: %w", output, err) 104 | } 105 | } 106 | opt = WithOutput(f) 107 | 108 | runtime.SetFinalizer(f, func(ff *os.File) { 109 | _ = ff.Sync() 110 | _ = ff.Close() 111 | }) 112 | default: 113 | utils.MakeDirIfNotExist(file) 114 | 115 | f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 116 | if err != nil { 117 | return func(c *options) error { 118 | return fmt.Errorf("unable to open/create file %q: %w", output, err) 119 | } 120 | } 121 | opt = WithOutput(f) 122 | 123 | runtime.SetFinalizer(f, func(ff *os.File) { 124 | _ = ff.Sync() 125 | _ = ff.Close() 126 | }) 127 | } 128 | 129 | return opt 130 | } 131 | 132 | // WithOutput configures the writer used to write logs to 133 | func WithOutput(writer io.Writer) Option { 134 | return func(opt *options) error { 135 | opt.logger.Out = writer 136 | return nil 137 | } 138 | } 139 | 140 | // WithSetReportCaller configures the logrus logger SetReportCaller 141 | func WithSetReportCaller(enable bool) Option { 142 | return func(opt *options) error { 143 | opt.logger.SetReportCaller(enable) 144 | return nil 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /internal/log/logrus.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | ) 6 | 7 | var logger *logrus.Logger 8 | 9 | // newLogger is a delegator method for logrus.New 10 | func newLogger() *logrus.Logger { 11 | return logrus.New() 12 | } 13 | -------------------------------------------------------------------------------- /internal/trace/search_file.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "mime/multipart" 9 | "net/http" 10 | "os" 11 | "path/filepath" 12 | "strconv" 13 | "time" 14 | 15 | "github.com/briandowns/spinner" 16 | "github.com/ci-monk/loli/internal/consts" 17 | log "github.com/ci-monk/loli/internal/log" 18 | "github.com/ci-monk/loli/internal/types" 19 | "github.com/ci-monk/loli/internal/utils" 20 | "github.com/fatih/color" 21 | "github.com/jedib0t/go-pretty/table" 22 | "github.com/muesli/termenv" 23 | ) 24 | 25 | // SearchAnimeByFile function 26 | func SearchAnimeByFile(animeFile string, pretty bool) { 27 | searchURL := consts.TraceMoeSearchAnimeByFile 28 | log.Infoln(searchURL) 29 | 30 | termenv.DefaultOutput().HideCursor() 31 | defer termenv.DefaultOutput().ShowCursor() 32 | 33 | s := spinner.New(spinner.CharSets[39], 100*time.Millisecond) 34 | s.Prefix = "🌊 Searching for the anime from an image: " 35 | s.FinalMSG = color.GreenString("✔️ Found!\n\n") 36 | 37 | go catchInterrupt(s) 38 | 39 | s.Start() 40 | 41 | if !utils.IsFileExists(animeFile) { 42 | log.Fatal("Invalid file path") 43 | } 44 | 45 | imageFile, error := os.Open(animeFile) 46 | if error != nil { 47 | log.Fatalln(error) 48 | } 49 | 50 | payload := &bytes.Buffer{} 51 | writer := multipart.NewWriter(payload) 52 | part, _ := writer.CreateFormFile("image", filepath.Base(animeFile)) 53 | 54 | _, error = io.Copy(part, imageFile) 55 | if error != nil { 56 | log.Fatalln(error) 57 | } 58 | 59 | error = writer.Close() 60 | if error != nil { 61 | log.Fatalln(error) 62 | } 63 | 64 | resp, error := http.Post(searchURL, writer.FormDataContentType(), payload) 65 | if error != nil { 66 | log.Fatalln(error) 67 | } 68 | defer resp.Body.Close() 69 | 70 | if resp.StatusCode != http.StatusOK { 71 | log.Fatalf("Bad status code - %d", resp.StatusCode) 72 | } 73 | 74 | content, err := io.ReadAll(resp.Body) 75 | if err != nil { 76 | log.Fatalln(err) 77 | } 78 | 79 | var animeResp types.Response 80 | err = json.Unmarshal(content, &animeResp) 81 | if err != nil { 82 | log.Fatalln(err) 83 | } 84 | 85 | s.Stop() 86 | 87 | if pretty { 88 | versionTable := table.NewWriter() 89 | versionTable.SetOutputMirror(os.Stdout) 90 | versionTable.AppendHeader(table.Row{"Info", "Content"}) 91 | versionTable.AppendRows([]table.Row{ 92 | {"📊 Similarity", utils.AnimeSimilarity(fmt.Sprintf("%f", animeResp.Result[0].Similarity))}, 93 | {"🌸 Title Native", animeResp.Result[0].Anilist.Title.Native}, 94 | {"🗽 Title English", animeResp.Result[0].Anilist.Title.English}, 95 | {"🗻 Title Romaji", animeResp.Result[0].Anilist.Title.Romaji}, 96 | {"📺 Episode Number", color.MagentaString(strconv.Itoa(animeResp.Result[0].Episode))}, 97 | {"😈 Is Adult", utils.AnimeIsAdult(animeResp.Result[0].Anilist.IsAdult)}, 98 | }) 99 | versionTable.SetStyle(table.StyleColoredBlueWhiteOnBlack) 100 | versionTable.Render() 101 | } else { 102 | fmt.Println("📊 Similarity: " + utils.AnimeSimilarity(fmt.Sprintf("%f", animeResp.Result[0].Similarity))) 103 | fmt.Println("🌸 Title Native: " + animeResp.Result[0].Anilist.Title.Native) 104 | fmt.Println("🗽 Title English: " + animeResp.Result[0].Anilist.Title.English) 105 | fmt.Println("🗻 Title Romaji: " + animeResp.Result[0].Anilist.Title.Romaji) 106 | fmt.Println("📺 Episode Number: " + color.MagentaString(strconv.Itoa(animeResp.Result[0].Episode))) 107 | fmt.Println("😈 Is Adult: " + utils.AnimeIsAdult(animeResp.Result[0].Anilist.IsAdult)) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /internal/trace/search_link.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "strconv" 12 | "time" 13 | 14 | "github.com/briandowns/spinner" 15 | "github.com/ci-monk/loli/internal/consts" 16 | log "github.com/ci-monk/loli/internal/log" 17 | "github.com/ci-monk/loli/internal/types" 18 | "github.com/ci-monk/loli/internal/utils" 19 | "github.com/fatih/color" 20 | "github.com/jedib0t/go-pretty/table" 21 | "github.com/muesli/termenv" 22 | ) 23 | 24 | // SearchAnimeByLink function 25 | func SearchAnimeByLink(animeLink string, pretty bool) { 26 | searchURL := consts.TraceMoeSearchAnimeByLink 27 | log.Info(searchURL) 28 | 29 | fullURL := searchURL + animeLink 30 | log.Infoln(fullURL) 31 | 32 | _, err := url.ParseRequestURI(searchURL) 33 | if err != nil { 34 | log.Error("Invalid url") 35 | } 36 | 37 | termenv.DefaultOutput().HideCursor() 38 | defer termenv.DefaultOutput().ShowCursor() 39 | 40 | s := spinner.New(spinner.CharSets[39], 100*time.Millisecond) 41 | s.Prefix = "🌊 Searching for the anime from a link: " 42 | s.FinalMSG = color.GreenString("✔️ Found!\n\n") 43 | 44 | go catchInterrupt(s) 45 | 46 | s.Start() 47 | 48 | reqBody, err := json.Marshal(map[string]string{}) 49 | if err != nil { 50 | log.Fatalln(err) 51 | } 52 | 53 | resp, err := http.Post(fullURL, "application/json", bytes.NewBuffer(reqBody)) 54 | if err != nil { 55 | log.Fatalln(err) 56 | } 57 | defer resp.Body.Close() 58 | 59 | if resp.StatusCode != http.StatusOK { 60 | log.Fatalf("Bad status code - %d", resp.StatusCode) 61 | } 62 | 63 | content, err := io.ReadAll(resp.Body) 64 | if err != nil { 65 | log.Fatalln(err) 66 | } 67 | 68 | var animeResp types.Response 69 | err = json.Unmarshal(content, &animeResp) 70 | if err != nil { 71 | log.Fatalln(err) 72 | } 73 | 74 | s.Stop() 75 | 76 | if pretty { 77 | versionTable := table.NewWriter() 78 | versionTable.SetOutputMirror(os.Stdout) 79 | versionTable.AppendHeader(table.Row{"Info", "Content"}) 80 | versionTable.AppendRows([]table.Row{ 81 | {"📊 Similarity", utils.AnimeSimilarity(fmt.Sprintf("%f", animeResp.Result[0].Similarity))}, 82 | {"🌸 Title Native", animeResp.Result[0].Anilist.Title.Native}, 83 | {"🗽 Title English", animeResp.Result[0].Anilist.Title.English}, 84 | {"🗻 Title Romaji", animeResp.Result[0].Anilist.Title.Romaji}, 85 | {"📺 Episode Number", color.MagentaString(strconv.Itoa(animeResp.Result[0].Episode))}, 86 | {"😈 Is Adult", utils.AnimeIsAdult(animeResp.Result[0].Anilist.IsAdult)}, 87 | }) 88 | versionTable.SetStyle(table.StyleColoredBlueWhiteOnBlack) 89 | versionTable.Render() 90 | } else { 91 | fmt.Println("📊 Similarity: " + utils.AnimeSimilarity(fmt.Sprintf("%f", animeResp.Result[0].Similarity))) 92 | fmt.Println("🌸 Title Native: " + animeResp.Result[0].Anilist.Title.Native) 93 | fmt.Println("🗽 Title English: " + animeResp.Result[0].Anilist.Title.English) 94 | fmt.Println("🗻 Title Romaji: " + animeResp.Result[0].Anilist.Title.Romaji) 95 | fmt.Println("📺 Episode Number: " + color.MagentaString(strconv.Itoa(animeResp.Result[0].Episode))) 96 | fmt.Println("😈 Is Adult: " + utils.AnimeIsAdult(animeResp.Result[0].Anilist.IsAdult)) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /internal/trace/search_usage.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | 11 | "github.com/ci-monk/loli/internal/consts" 12 | log "github.com/ci-monk/loli/internal/log" 13 | "github.com/ci-monk/loli/internal/types" 14 | "github.com/fatih/color" 15 | "github.com/jedib0t/go-pretty/table" 16 | ) 17 | 18 | // SearchUsage function 19 | func SearchUsage(pretty bool) { 20 | searchURL := consts.TraceMoeUsage 21 | log.Infoln(searchURL) 22 | 23 | resp, error := http.Get(searchURL) 24 | if error != nil { 25 | log.Fatalln(error) 26 | } 27 | defer resp.Body.Close() 28 | 29 | if resp.StatusCode != http.StatusOK { 30 | log.Fatalf("Bad status code - %d", resp.StatusCode) 31 | } 32 | 33 | content, err := io.ReadAll(resp.Body) 34 | if err != nil { 35 | log.Fatalln(err) 36 | } 37 | 38 | var usageResp types.UsageTraceMoe 39 | err = json.Unmarshal(content, &usageResp) 40 | if err != nil { 41 | log.Fatalln(err) 42 | } 43 | 44 | if pretty { 45 | versionTable := table.NewWriter() 46 | versionTable.SetOutputMirror(os.Stdout) 47 | versionTable.AppendHeader(table.Row{"Info", "Content"}) 48 | versionTable.AppendRows([]table.Row{ 49 | {"💻 IP", usageResp.ID}, 50 | {"🧾 Priority", strconv.Itoa(usageResp.Priority)}, 51 | {"📚 Concurrency", strconv.Itoa(usageResp.Concurrency)}, 52 | {"📂 Quota", strconv.Itoa(usageResp.Quota)}, 53 | {"📍 QuotaUsed", color.MagentaString(strconv.Itoa(usageResp.QuotaUsed))}, 54 | }) 55 | versionTable.SetStyle(table.StyleColoredBlueWhiteOnBlack) 56 | versionTable.Render() 57 | } else { 58 | fmt.Println("💻 IP: " + usageResp.ID) 59 | fmt.Println("🧾 Priority: " + strconv.Itoa(usageResp.Priority)) 60 | fmt.Println("📚 Concurrency: " + strconv.Itoa(usageResp.Concurrency)) 61 | fmt.Println("📂 Quota: " + strconv.Itoa(usageResp.Quota)) 62 | fmt.Println("📍 QuotaUsed: " + color.MagentaString(strconv.Itoa(usageResp.QuotaUsed))) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /internal/trace/signals.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | 8 | "github.com/briandowns/spinner" 9 | "github.com/fatih/color" 10 | "github.com/muesli/termenv" 11 | ) 12 | 13 | func catchInterrupt(s *spinner.Spinner) { 14 | sigs := make(chan os.Signal, 1) 15 | signal.Notify(sigs, syscall.SIGINT) 16 | 17 | <-sigs 18 | if s.Active() { 19 | s.FinalMSG = color.GreenString("👋 Bye!\n") 20 | s.Stop() 21 | } 22 | 23 | termenv.DefaultOutput().ShowCursor() 24 | os.Exit(0) 25 | } 26 | -------------------------------------------------------------------------------- /internal/types/response.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // AnimeTitle struct - information about the anime title 4 | type AnimeTitle struct { 5 | Native string `json:"native"` 6 | Romaji string `json:"romaji"` 7 | English string `json:"english"` 8 | } 9 | 10 | // Anilist struct - information about the anime passed in the request to trace.moe API 11 | type Anilist struct { 12 | ID int `json:"id"` 13 | IDMal int `json:"idMal"` 14 | Title AnimeTitle `json:"title"` 15 | Synonyms []string `json:"synonyms"` 16 | IsAdult bool `json:"isAdult"` 17 | } 18 | 19 | type Anime struct { 20 | Anilist Anilist `json:"anilist"` 21 | Filename string `json:"filename"` 22 | Episode int `json:"episode"` 23 | From float32 `json:"from"` 24 | To float32 `json:"to"` 25 | Similarity float64 `json:"similarity"` 26 | Video string `json:"video"` 27 | Image string `json:"image"` 28 | } 29 | 30 | // Response struct - content of trace.moe API request result 31 | type Response struct { 32 | FrameCount int `json:"frameCount"` 33 | Error string `json:"error"` 34 | Result []Anime `json:"result"` 35 | } 36 | 37 | // UsageTraceMoe struct - content of request usage to trace moe 38 | type UsageTraceMoe struct { 39 | ID string `json:"id"` 40 | Priority int `json:"priority"` 41 | Concurrency int `json:"concurrency"` 42 | Quota int `json:"quota"` 43 | QuotaUsed int `json:"quotaUsed"` 44 | } 45 | -------------------------------------------------------------------------------- /internal/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strconv" 8 | "strings" 9 | "time" 10 | 11 | "github.com/charmbracelet/glamour" 12 | "github.com/ci-monk/loli/internal/consts" 13 | "github.com/fatih/color" 14 | "github.com/kyokomi/emoji/v2" 15 | au "github.com/logrusorgru/aurora" 16 | "github.com/sirupsen/logrus" 17 | ) 18 | 19 | type markdownRenderOpts []glamour.TermRendererOption 20 | 21 | // RenderMarkdown function that return a pretty markdown in output 22 | func RenderMarkdown(text string) (string, error) { 23 | opts := markdownRenderOpts{ 24 | glamour.WithAutoStyle(), 25 | glamour.WithWordWrap(75), 26 | glamour.WithEmoji(), 27 | } 28 | return renderMarkdown(text, opts) 29 | } 30 | 31 | func renderMarkdown(text string, opts markdownRenderOpts) (string, error) { 32 | text = strings.ReplaceAll(text, "\r\n", "\n") 33 | 34 | tr, err := glamour.NewTermRenderer(opts...) 35 | if err != nil { 36 | return "", err 37 | } 38 | 39 | out, err := tr.Render(text) 40 | if err != nil { 41 | return "", err 42 | } 43 | 44 | return out, nil 45 | } 46 | 47 | // IsEmpty function - check if a string is empty 48 | func IsEmpty(value string) bool { 49 | return len(strings.TrimSpace(value)) == 0 50 | } 51 | 52 | // IsDirExists function - check fi a directory exist in te system 53 | func IsDirExists(path string) bool { 54 | result, err := os.Stat(path) 55 | if err != nil { 56 | return os.IsExist(err) 57 | } 58 | return result.IsDir() 59 | } 60 | 61 | // IsFileExists function - check fi a file exist in te system 62 | func IsFileExists(name string) bool { 63 | if _, err := os.Stat(name); err != nil { 64 | if os.IsNotExist(err) { 65 | return false 66 | } 67 | } 68 | return true 69 | } 70 | 71 | // MakeDirIfNotExist create a new directory if they not exist 72 | func MakeDirIfNotExist(dir string) { 73 | fullDir, _ := filepath.Abs(filepath.Dir(dir)) 74 | mode := os.FileMode(0775) 75 | if !IsDirExists(fullDir) { 76 | err := os.MkdirAll(fullDir, mode) 77 | if err != nil { 78 | logrus.Fatal(err) 79 | } 80 | } 81 | } 82 | 83 | // CreateLogFile function 84 | func CreateLogFile(logdir, logfile string) string { 85 | pid := strconv.Itoa(os.Getpid()) 86 | return filepath.Join(logdir, 87 | logfile+ 88 | ".pid"+ 89 | pid+ 90 | "."+ 91 | time.Now().Format(consts.DefaultTimestampFormat)+ 92 | ".log", 93 | ) 94 | } 95 | 96 | // AnimeSimilarity is for colorful output 97 | func AnimeSimilarity(similarity string) string { 98 | if similarity > "0.89" { 99 | return fmt.Sprintf("😃 "+"%s", au.Green( 100 | emoji.Sprintf(similarity), 101 | ).Bold()) 102 | } else if similarity > "0.80" { 103 | return fmt.Sprintf("😞 "+"%s", au.Yellow( 104 | emoji.Sprintf(similarity), 105 | ).Bold()) 106 | } else { 107 | return fmt.Sprintf("😡 "+"%s", au.Red( 108 | emoji.Sprintf(similarity), 109 | ).Bold()) 110 | } 111 | } 112 | 113 | // AnimeIsAdult is for colorful output 114 | func AnimeIsAdult(isAdult bool) string { 115 | if isAdult { 116 | return fmt.Sprint(color.GreenString("true")) 117 | } 118 | return fmt.Sprint(color.RedString("false")) 119 | } 120 | 121 | // ShowHeader pretty print header information 122 | func ShowHeader() { 123 | outputRender, err := RenderMarkdown(consts.Welcome) 124 | if err != nil { 125 | fmt.Fprintf(os.Stderr, "Error render glamour markdow: %v\n", err) 126 | os.Exit(1) 127 | } 128 | fmt.Print(outputRender) 129 | } 130 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/ci-monk/loli/internal/consts" 7 | "github.com/jedib0t/go-pretty/table" 8 | "github.com/pterm/pterm" 9 | ) 10 | 11 | // Default build-time variable. These variables are populated via the Go ldflags. This will be filled in by the compiler 12 | var ( 13 | cliName = consts.BinaryName // default name for this CLI 14 | cliVersion = "0.0.0" // value from VERSION file 15 | builtDate = "1970-01-01T00:00:00Z" // output from `date -u +'%Y-%m-%dT%H:%M:%SZ'` 16 | builtBy = "unknown-built-by" // built agent (GoRelease, Makefile...) 17 | commit = "unknown-commit" // output from `git rev-parse HEAD` 18 | commitShort = "unknown-short-commit" // output from `git rev-parse --short HEAD` 19 | commitBranch = "unknown-commit-branch" // output from `git rev-parse --abbrev-ref HEAD` 20 | projectURL = consts.ProjectURL // github project url 21 | goVersion = "unknown-go-version" // output from `go version` 22 | ) 23 | 24 | // GetVersionFormatted function 25 | func GetVersionFormatted() string { 26 | return cliVersion 27 | } 28 | 29 | // GetShortDetails function - create a pretty table and parse this table with current version details 30 | func GetShortDetails() { 31 | pterm.Println() 32 | pterm.DefaultHeader. 33 | WithMargin(5). 34 | WithBackgroundStyle(pterm.NewStyle(pterm.BgBlack)). 35 | Printf("✨ %s version details ✨", consts.BinaryName) 36 | pterm.Println() 37 | versionTable := table.NewWriter() 38 | versionTable.SetOutputMirror(os.Stdout) 39 | versionTable.AppendHeader(table.Row{"Info", "Content"}) 40 | versionTable.AppendRows([]table.Row{ 41 | {"➤ CLI Name", cliName}, 42 | {"➤ CLI Version", cliVersion}, 43 | {"➤ Project URL", projectURL}, 44 | {"➤ Build Date", builtDate}, 45 | {"➤ Commit Short", commitShort}, 46 | {"➤ Go Version", goVersion}, 47 | }) 48 | versionTable.SetStyle(table.StyleColoredBright) 49 | versionTable.Render() 50 | pterm.Println() 51 | } 52 | 53 | // GetPrettyDetails function - create a pretty table and parse this table with current version details 54 | func GetPrettyDetails() { 55 | pterm.Println() 56 | pterm.DefaultHeader. 57 | WithMargin(5). 58 | WithBackgroundStyle(pterm.NewStyle(pterm.BgBlack)). 59 | Printf("✨ %s version details ✨", consts.BinaryName) 60 | pterm.Println() 61 | versionTable := table.NewWriter() 62 | versionTable.SetOutputMirror(os.Stdout) 63 | versionTable.AppendHeader(table.Row{"Info", "Content"}) 64 | versionTable.AppendRows([]table.Row{ 65 | {"➤ CLI Name", cliName}, 66 | {"➤ CLI Version", cliVersion}, 67 | {"➤ Project URL", projectURL}, 68 | {"➤ Build Date", builtDate}, 69 | {"➤ Build by", builtBy}, 70 | {"➤ Commit", commit}, 71 | {"➤ Commit Short", commitShort}, 72 | {"➤ Commit Branch", commitBranch}, 73 | {"➤ Go Version", goVersion}, 74 | }) 75 | versionTable.SetStyle(table.StyleColoredBright) 76 | versionTable.Render() 77 | pterm.Println() 78 | } 79 | 80 | // ShowVersion function - check detail flag and show the pretty details if enabled (`true`) 81 | func ShowVersion(pretty bool) { 82 | if pretty { 83 | GetPrettyDetails() 84 | } else { 85 | pterm.Println() 86 | pterm.DefaultHeader. 87 | WithMargin(5). 88 | WithBackgroundStyle(pterm.NewStyle(pterm.BgBlack)). 89 | Printf("✨ %s version %s ✨\n\n (%s)", cliName, cliVersion, builtDate) 90 | pterm.Println() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /scripts/completions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | rm -rf completions && mkdir completions 5 | 6 | for sh in bash zsh fish; do 7 | go run ./cmd/loli/main.go completion "$sh" >"completions/loli.$sh" 8 | done 9 | --------------------------------------------------------------------------------