diff --git a/.github/ISSUE_TEMPLATE/BugReport.yml b/.github/ISSUE_TEMPLATE/BugReport.yml
new file mode 100644
index 0000000..c6dbede
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/BugReport.yml
@@ -0,0 +1,51 @@
+name: Bug Report
+description: Report a bug
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill out the sections below to help everyone identify and fix the bug
+ - type: textarea
+ id: description
+ attributes:
+ label: Describe your issue
+ placeholder: When I click here this happens
+ validations:
+ required: true
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to reproduce
+ placeholder: |
+ 1. Enhance this function
+ 2. Refactor this
+ 3. Refactor that
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: What was the expected result?
+ placeholder: I expected this to happen
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Put here any screenshots or videos (optional)
+ - type: textarea
+ id: deviceinfo
+ attributes:
+ label: Your OS and Browser (optional)
+ placeholder: |
+ - OS: Windows 10
+ - Browser: Chrome 90.0
+ - type: dropdown
+ id: assign
+ attributes:
+ label: "Would you like to work on this issue?"
+ options:
+ - "Yes"
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for reporting this issue! We will get back to you as soon as possible.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/NewFeature.yml b/.github/ISSUE_TEMPLATE/NewFeature.yml
new file mode 100644
index 0000000..e284c58
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/NewFeature.yml
@@ -0,0 +1,36 @@
+name: New feature
+description: Suggest or request a new feature
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill out the sections below to properly describe the new feature you are suggesting.
+ - type: textarea
+ id: description
+ attributes:
+ label: Describe the feature
+ placeholder: A feature X that allows to do Y
+ validations:
+ required: true
+ - type: textarea
+ id: rationale
+ attributes:
+ label: It should be implemented because
+ placeholder: It will allow to do Y that is needed for Z
+ - type: textarea
+ id: context
+ attributes:
+ label: Additional context
+ placeholder: |
+ Add any other context or screenshots about the feature request here.
+ - type: dropdown
+ id: assign
+ attributes:
+ label: "Would you like to work on this issue?"
+ options:
+ - "Yes"
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for your suggestion! Let's see together if it can be implemented.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/README.md b/.github/ISSUE_TEMPLATE/README.md
new file mode 100644
index 0000000..622d9f5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/README.md
@@ -0,0 +1,15 @@
+# Issues template
+
+I created this template to help you report bugs or suggest new features more easily without having to manually write them.
+
+If you have any questions, please submit a Pull Request (PR) or open Issues.
+
+
+
+
You can choose one of these three
+
+
+Bug Report
+
+
+New Feature
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..c7436f2
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,91 @@
+name: Release Go Package
+
+on:
+ push:
+ branches:
+ - main
+ - fix/download-version
+ - test/ci-cd
+ tags:
+ - 'v*'
+ pull_request:
+ branches: [ '**' ]
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: write
+
+jobs:
+ test-and-release:
+ name: Quality & Release
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.24'
+ cache: true
+
+ - name: Run Linter
+ uses: golangci/golangci-lint-action@v6
+ with:
+ version: latest
+
+ # Quick Test: Runs on any Pull Request, or during Release/Test-branch
+ - name: Quick Test
+ if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v') || github.ref_name == 'test/ci-cd'
+ run: go test -v ./...
+
+ # Deep Test (-race): Runs only when PR targets main, or during Release/Test-branch
+ - name: Deep Test (Race Detection)
+ if: (github.event_name == 'pull_request' && github.base_ref == 'main') || startsWith(github.ref, 'refs/tags/v') || github.ref_name == 'test/ci-cd'
+ run: go test -v -race $(go list ./... | grep -v /bench)
+
+ # Benchmark: Runs only on Releases (tags), test/ci-cd branch, or manual dispatch
+ - name: Run Benchmark & Capture Output
+ if: startsWith(github.ref, 'refs/tags/v') || github.ref_name == 'test/ci-cd' || github.event_name == 'workflow_dispatch'
+ run: |
+ echo "BENCH_REPORT<> $GITHUB_ENV
+ go run scripts/bench_analyzer.go >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+
+ # Prepare combined release notes
+ - name: Prepare Release Notes
+ if: startsWith(github.ref, 'refs/tags/v')
+ run: |
+ # 1. Generate standard GitHub release notes (What's Changed, Contributors, etc.)
+ GEN_NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes -f tag_name=${{ github.ref_name }} --jq .body)
+
+ # 2. Build the final body
+ echo "RELEASE_BODY<> $GITHUB_ENV
+ echo "$GEN_NOTES" >> $GITHUB_ENV
+ echo "" >> $GITHUB_ENV
+ echo "## Installation" >> $GITHUB_ENV
+ echo "\`\`\`bash" >> $GITHUB_ENV
+ echo "go get github.com/${{ github.repository }}@${{ github.ref_name }}" >> $GITHUB_ENV
+ echo "\`\`\`" >> $GITHUB_ENV
+ echo "" >> $GITHUB_ENV
+ echo "## Performance Benchmarks" >> $GITHUB_ENV
+ echo "\`\`\`text" >> $GITHUB_ENV
+ echo "${{ env.BENCH_REPORT }}" >> $GITHUB_ENV
+ echo "\`\`\`" >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Create GitHub Release
+ if: startsWith(github.ref, 'refs/tags/v')
+ uses: softprops/action-gh-release@v1
+ with:
+ name: ${{ github.ref_name }}
+ body: ${{ env.RELEASE_BODY }}
+ generate_release_notes: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/go.mod b/go.mod
index 4745352..0242d01 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
module github.com/versenilvis/fuzzy
-go 1.26.2
+go 1.24
diff --git a/scripts/bench_analyzer.go b/scripts/bench_analyzer.go
index 03e77c4..9c1edcc 100644
--- a/scripts/bench_analyzer.go
+++ b/scripts/bench_analyzer.go
@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"fmt"
+ "os"
"os/exec"
"regexp"
"strconv"
@@ -18,7 +19,7 @@ type benchResult struct {
}
func main() {
- fmt.Println("Running benchmarks, please wait...")
+ fmt.Fprintln(os.Stderr, "Running benchmarks, please wait...")
cmd := exec.Command("go", "test", "-bench=.", "-benchmem", "./bench/...")
var out bytes.Buffer
@@ -44,24 +45,24 @@ func main() {
func parseBenchOutput(output string) []benchResult {
var results []benchResult
scanner := bufio.NewScanner(strings.NewReader(output))
-
+
// regex to parse: Name-Threads Iterations Time/op Mem/op Allocs/op
re := regexp.MustCompile(`Benchmark([\w/]+)-?\d*\s+\d+\s+([\d\.]+)\sns/op\s+([\d\.]+)\sB/op`)
descriptions := map[string]string{
- "Linux100k/ascii_short": "Real-world dataset (100K files)",
- "Linux100k/ascii_ext": "Extended ASCII search",
- "Linux100k/path_like": "Deep nesting (/usr/lib/...)",
- "Linux100k/typo": "Typo-tolerant search",
- "Linux100k/long_query": "Long query pattern",
- "Linux100k_NewSearcher": "Build index (~100K files)",
- "Search/1000_files": "Standard workload (1K files)",
- "Search/10000_files": "Medium workload (10K files)",
- "Normalize": "String cleaning (ASCII)",
- "LevenshteinRatio": "Zero allocation core distance",
- "SearchWithCache": "Search with internal cache",
- "GetBoostScores": "Ranking logic (Frecency)",
- "RecordSelection": "Updating selection history",
+ "Linux100k/ascii_short": "Real-world dataset (100K files)",
+ "Linux100k/ascii_ext": "Extended ASCII search",
+ "Linux100k/path_like": "Deep nesting (/usr/lib/...)",
+ "Linux100k/typo": "Typo-tolerant search",
+ "Linux100k/long_query": "Long query pattern",
+ "Linux100k_NewSearcher": "Build index (~100K files)",
+ "Search/1000_files": "Standard workload (1K files)",
+ "Search/10000_files": "Medium workload (10K files)",
+ "Normalize": "String cleaning (ASCII)",
+ "LevenshteinRatio": "Zero allocation core distance",
+ "SearchWithCache": "Search with internal cache",
+ "GetBoostScores": "Ranking logic (Frecency)",
+ "RecordSelection": "Updating selection history",
}
for scanner.Scan() {
diff --git a/tests/fuzzy_test.go b/tests/fuzzy_test.go
index ff79259..913cb3c 100644
--- a/tests/fuzzy_test.go
+++ b/tests/fuzzy_test.go
@@ -213,20 +213,24 @@ func TestSearcher_EdgeCases(t *testing.T) {
s := fuzzy.NewSearcher(files)
var wg sync.WaitGroup
- for range 50 {
- wg.Go(func() {
- for range 100 {
+ for range 20 {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for range 50 {
s.Search("aaa")
}
- })
+ }()
}
- for range 10 {
- wg.Go(func() {
- for range 50 {
+ for range 5 {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for range 20 {
s.RecordSelection("aaa", files[0])
}
- })
+ }()
}
wg.Wait()