├── .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 |

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 |

9 |
10 | Hello Humans 👽! Loli is a pretty CLI that search animes passing images or links
11 |
12 | [](https://www.conventionalcommits.org/en/v1.0.0/) [](https://semantic-release.gitbook.io/semantic-release/usage/configuration) [](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 |

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 |
--------------------------------------------------------------------------------