Merge pull request #1 from Snider/copilot/create-go-package-cli
Create modular Go package with CLI for miner management
This commit is contained in:
commit
00ca76f6f2
17 changed files with 1380 additions and 1 deletions
50
.coderabbit.yaml
Normal file
50
.coderabbit.yaml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# CodeRabbit Configuration
|
||||
# https://docs.coderabbit.ai/guides/configure-coderabbit
|
||||
|
||||
language: en-US
|
||||
|
||||
reviews:
|
||||
auto_review:
|
||||
enabled: true
|
||||
drafts: false
|
||||
base_branches:
|
||||
- main
|
||||
- master
|
||||
- develop
|
||||
|
||||
request_changes_workflow: false
|
||||
high_level_summary: true
|
||||
poem: false
|
||||
review_status: true
|
||||
collapse_walkthrough: false
|
||||
|
||||
path_filters:
|
||||
- "!**/*.md"
|
||||
- "!**/*.txt"
|
||||
- "!**/testdata/**"
|
||||
|
||||
path_instructions:
|
||||
- path: "**/*.go"
|
||||
instructions: |
|
||||
- Follow Go best practices and idiomatic patterns
|
||||
- Ensure proper error handling
|
||||
- Check for goroutine leaks and race conditions
|
||||
- Verify nil pointer checks
|
||||
- Ensure proper resource cleanup (defer statements)
|
||||
|
||||
- path: "**/*_test.go"
|
||||
instructions: |
|
||||
- Verify test coverage is adequate
|
||||
- Check for table-driven tests where appropriate
|
||||
- Ensure proper test cleanup
|
||||
- Validate edge cases are tested
|
||||
|
||||
chat:
|
||||
auto_reply: true
|
||||
|
||||
knowledge_base:
|
||||
learnings:
|
||||
scope: auto
|
||||
|
||||
early_access: false
|
||||
enable_free_tier: true
|
||||
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
coverage.html
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# Build artifacts
|
||||
dist/
|
||||
build/
|
||||
bin/
|
||||
*.tar.gz
|
||||
*.zip
|
||||
mining
|
||||
|
||||
# IDE specific files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# goreleaser
|
||||
.goreleaser.yaml.bak
|
||||
100
.goreleaser.yaml
Normal file
100
.goreleaser.yaml
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# GoReleaser configuration
|
||||
# https://goreleaser.com
|
||||
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go test ./...
|
||||
|
||||
builds:
|
||||
- id: mining-cli
|
||||
main: ./cmd/mining
|
||||
binary: mining
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
goarm:
|
||||
- "7"
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/Snider/Mining/pkg/mining.version={{.Version}}
|
||||
- -X github.com/Snider/Mining/pkg/mining.commit={{.Commit}}
|
||||
- -X github.com/Snider/Mining/pkg/mining.date={{.Date}}
|
||||
|
||||
archives:
|
||||
- id: mining
|
||||
name_template: >-
|
||||
{{ .ProjectName }}_
|
||||
{{- title .Os }}_
|
||||
{{- if eq .Arch "amd64" }}x86_64
|
||||
{{- else if eq .Arch "386" }}i386
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
version_template: "{{ incpatch .Version }}-next"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
use: github
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore:'
|
||||
- 'merge conflict'
|
||||
- Merge pull request
|
||||
- Merge remote-tracking branch
|
||||
- Merge branch
|
||||
groups:
|
||||
- title: 'New Features'
|
||||
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
||||
order: 0
|
||||
- title: 'Bug fixes'
|
||||
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
|
||||
order: 1
|
||||
- title: 'Performance Improvements'
|
||||
regexp: '^.*?perf(\([[:word:]]+\))??!?:.+$'
|
||||
order: 2
|
||||
- title: 'Documentation updates'
|
||||
regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$'
|
||||
order: 3
|
||||
- title: Other work
|
||||
order: 999
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: Snider
|
||||
name: Mining
|
||||
draft: false
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
header: |
|
||||
## Mining Release {{ .Tag }}
|
||||
|
||||
**Full Changelog**: https://github.com/Snider/Mining/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||
|
||||
# Generate SBOM (Software Bill of Materials)
|
||||
sboms:
|
||||
- artifacts: archive
|
||||
|
||||
# Announce releases
|
||||
announce:
|
||||
skip: false
|
||||
267
LICENSE
Normal file
267
LICENSE
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
European Union Public Licence V. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the 'EUPL') applies to the Work (as defined below)
|
||||
which is provided under the terms of this Licence. Any use of the Work, other than as
|
||||
authorised under this Licence is prohibited (to the extent such use is covered by a
|
||||
right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as defined
|
||||
below) has placed the following notice immediately following the copyright notice for
|
||||
the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
— 'The Licence': this Licence.
|
||||
— 'The Original Work': the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable Code as
|
||||
the case may be.
|
||||
— 'Derivative Works': the works or software that could be created by the Licensee,
|
||||
based upon the Original Work or modifications thereof. This Licence does not define
|
||||
the extent of modification or dependence on the Original Work required in order to
|
||||
classify a work as a Derivative Work; this extent is determined by copyright law
|
||||
applicable in the country mentioned in Article 15.
|
||||
— 'The Work': the Original Work or its Derivative Works.
|
||||
— 'The Source Code': the human-readable form of the Work which is the most convenient
|
||||
for people to study and modify.
|
||||
— 'The Executable Code': any code which has generally been compiled and which is meant
|
||||
to be interpreted by a computer as a program.
|
||||
— 'The Licensor': the natural or legal person that distributes or communicates the
|
||||
Work under the Licence.
|
||||
— 'Contributor(s)': any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
— 'The Licensee' or 'You': any natural or legal person who makes any usage of the Work
|
||||
under the terms of the Licence.
|
||||
— 'Distribution' or 'Communication': any act of selling, giving, lending, renting,
|
||||
distributing, communicating, transmitting, or otherwise making available, online or
|
||||
offline, copies of the Work or providing access to its essential functionalities at
|
||||
the disposal of any other natural or legal person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable
|
||||
licence to do the following, for the duration of copyright vested in the Original Work:
|
||||
|
||||
— use the Work in any circumstance and for all usage,
|
||||
— reproduce the Work,
|
||||
— modify the Work, and make Derivative Works based upon the Work,
|
||||
— communicate to the public, including the right to make available or display the Work
|
||||
or copies thereof to the public and perform publicly, as the case may be, the Work,
|
||||
— distribute the Work or copies thereof,
|
||||
— lend and rent the Work or copies thereof,
|
||||
— sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now known or
|
||||
later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to exercise
|
||||
his moral right to the extent allowed by law in order to make effective the licence of
|
||||
the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any
|
||||
patents held by the Licensor, to the extent necessary to make use of the rights
|
||||
granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as Executable
|
||||
Code. If the Work is provided as Executable Code, the Licensor provides in addition a
|
||||
machine-readable copy of the Source Code of the Work along with each copy of the Work
|
||||
that the Licensor distributes or indicates, in a notice following the copyright notice
|
||||
attached to the Work, a repository where the Source Code is easily and freely
|
||||
accessible for as long as the Licensor continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from any
|
||||
exception or limitation to the exclusive rights of the rights owners in the Work, of
|
||||
the exhaustion of those rights or of other applicable limitations thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and obligations
|
||||
imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or trademarks
|
||||
notices and all notices that refer to the Licence and to the disclaimer of warranties.
|
||||
The Licensee must include a copy of such notices and a copy of the Licence with every
|
||||
copy of the Work he/she distributes or communicates. The Licensee must cause any
|
||||
Derivative Work to carry prominent notices stating that the Work has been modified and
|
||||
the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the Original
|
||||
Works or Derivative Works, this Distribution or Communication will be done under the
|
||||
terms of this Licence or of a later version of this Licence unless the Original Work is
|
||||
expressly distributed only under this version of the Licence — for example by
|
||||
communicating 'EUPL v. 1.2 only'. The Licensee (becoming Licensor) cannot offer or
|
||||
impose any additional terms or conditions on the Work or Derivative Work that alter or
|
||||
restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or
|
||||
copies thereof based upon both the Work and another work licensed under a Compatible
|
||||
Licence, this Distribution or Communication can be done under the terms of this
|
||||
Compatible Licence. For the sake of this clause, 'Compatible Licence' refers to the
|
||||
licences listed in the appendix attached to this Licence. Should the Licensee's
|
||||
obligations under the Compatible Licence conflict with his/her obligations under this
|
||||
Licence, the obligations of the Compatible Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work, the
|
||||
Licensee will provide a machine-readable copy of the Source Code or indicate a
|
||||
repository where this Source will be easily and freely available for as long as the
|
||||
Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for reasonable
|
||||
and customary use in describing the origin of the Work and reproducing the content of
|
||||
the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the power and
|
||||
authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings to the
|
||||
Work are owned by him/her or licensed to him/her and that he/she has the power and
|
||||
authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent Contributors
|
||||
grant You a licence to their contributions to the Work, under the terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous Contributors.
|
||||
It is not a finished work and may therefore contain defects or 'bugs' inherent to this
|
||||
type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an 'as is' basis and
|
||||
without warranties of any kind concerning the Work, including without limitation
|
||||
merchantability, fitness for a particular purpose, absence of defects or errors,
|
||||
accuracy, non-infringement of intellectual property rights other than copyright as
|
||||
stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition for the
|
||||
grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural persons,
|
||||
the Licensor will in no event be liable for any direct or indirect, material or moral,
|
||||
damages of any kind, arising out of the Licence or of the use of the Work, including
|
||||
without limitation, damages for loss of goodwill, work stoppage, computer failure or
|
||||
malfunction, loss of data or any commercial damage, even if the Licensor has been
|
||||
advised of the possibility of such damage. However, the Licensor will be liable under
|
||||
statutory product liability laws as far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if accepting
|
||||
obligations, You may act only on your own behalf and on your sole responsibility, not
|
||||
on behalf of the original Licensor or any other Contributor, and only if You agree to
|
||||
indemnify, defend, and hold each Contributor harmless for any liability incurred by, or
|
||||
claims asserted against such Contributor by the fact You have accepted any warranty or
|
||||
additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon 'I agree' placed
|
||||
under the bottom of a window displaying the text of this Licence or by affirming
|
||||
consent in any other similar way, in accordance with the rules of applicable law. Clicking
|
||||
on that icon indicates your clear and irrevocable acceptance of this Licence and all of
|
||||
its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and conditions by
|
||||
exercising any rights granted to You by Article 2 of this Licence, such as the use of
|
||||
the Work, the creation by You of a Derivative Work or the Distribution or Communication
|
||||
by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a remote
|
||||
location) the distribution channel or media (for example, a website) must at least
|
||||
provide to the public the information requested by the applicable law regarding the
|
||||
Licensor, the Licence and the way it may be accessible, concluded, stored and
|
||||
reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon any
|
||||
breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has received the
|
||||
Work from the Licensee under the Licence, provided such persons remain in full
|
||||
compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete agreement
|
||||
between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable law, this
|
||||
will not affect the validity or enforceability of the Licence as a whole. Such provision
|
||||
will be construed or reformed so as necessary to make it valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of this
|
||||
Licence or updated versions of the Appendix, so far this is required and reasonable,
|
||||
without reducing the scope of the rights granted by the Licence. New versions of the
|
||||
Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission, have
|
||||
identical value. Parties can take advantage of the linguistic version of their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
— any litigation resulting from the interpretation of this License, arising between the
|
||||
European Union institutions, bodies, offices or agencies, as a Licensor, and any
|
||||
Licensee, will be subject to the jurisdiction of the Court of Justice of the European
|
||||
Union, as laid down in article 272 of the Treaty on the Functioning of the European
|
||||
Union,
|
||||
|
||||
— any litigation arising between other parties and resulting from the interpretation of
|
||||
this License, will be subject to the exclusive jurisdiction of the competent court
|
||||
where the Licensor resides or conducts its primary business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
— this Licence shall be governed by the law of the European Union Member State where
|
||||
the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
— this licence shall be governed by Belgian law if the Licensor has no seat, residence
|
||||
or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
'Compatible Licences' according to Article 5 EUPL are:
|
||||
|
||||
— GNU General Public License (GPL) v. 2, v. 3
|
||||
— GNU Affero General Public License (AGPL) v. 3
|
||||
— Open Software License (OSL) v. 2.1, v. 3.0
|
||||
— Eclipse Public License (EPL) v. 1.0
|
||||
— CeCILL v. 2.0, v. 2.1
|
||||
— Mozilla Public Licence (MPL) v. 2
|
||||
— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works
|
||||
other than software
|
||||
— European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity
|
||||
(LiLiQ-R+)
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide the
|
||||
rights granted in Article 2 of this Licence and protect the covered Source Code from
|
||||
exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new EUPL
|
||||
version.
|
||||
103
Makefile
Normal file
103
Makefile
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
.PHONY: all build test clean install run demo help lint fmt vet
|
||||
|
||||
# Variables
|
||||
BINARY_NAME=mining
|
||||
MAIN_PACKAGE=./cmd/mining
|
||||
GO=go
|
||||
GOFLAGS=-v
|
||||
|
||||
all: test build
|
||||
|
||||
# Build the CLI binary
|
||||
build:
|
||||
@echo "Building $(BINARY_NAME)..."
|
||||
$(GO) build $(GOFLAGS) -o $(BINARY_NAME) $(MAIN_PACKAGE)
|
||||
|
||||
# Build for multiple platforms
|
||||
build-all:
|
||||
@echo "Building for multiple platforms..."
|
||||
GOOS=linux GOARCH=amd64 $(GO) build -o dist/$(BINARY_NAME)-linux-amd64 $(MAIN_PACKAGE)
|
||||
GOOS=linux GOARCH=arm64 $(GO) build -o dist/$(BINARY_NAME)-linux-arm64 $(MAIN_PACKAGE)
|
||||
GOOS=darwin GOARCH=amd64 $(GO) build -o dist/$(BINARY_NAME)-darwin-amd64 $(MAIN_PACKAGE)
|
||||
GOOS=darwin GOARCH=arm64 $(GO) build -o dist/$(BINARY_NAME)-darwin-arm64 $(MAIN_PACKAGE)
|
||||
GOOS=windows GOARCH=amd64 $(GO) build -o dist/$(BINARY_NAME)-windows-amd64.exe $(MAIN_PACKAGE)
|
||||
|
||||
# Install the binary
|
||||
install:
|
||||
@echo "Installing $(BINARY_NAME)..."
|
||||
$(GO) install $(MAIN_PACKAGE)
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
@echo "Running tests..."
|
||||
$(GO) test -v -race -coverprofile=coverage.out ./...
|
||||
|
||||
# Run tests with coverage report
|
||||
coverage: test
|
||||
@echo "Generating coverage report..."
|
||||
$(GO) tool cover -html=coverage.out -o coverage.html
|
||||
|
||||
# Run demo
|
||||
demo:
|
||||
@echo "Running demo..."
|
||||
$(GO) run main.go
|
||||
|
||||
# Run the CLI
|
||||
run: build
|
||||
./$(BINARY_NAME)
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
@echo "Cleaning..."
|
||||
rm -f $(BINARY_NAME)
|
||||
rm -rf dist/
|
||||
rm -f coverage.out coverage.html
|
||||
$(GO) clean
|
||||
|
||||
# Format code
|
||||
fmt:
|
||||
@echo "Formatting code..."
|
||||
$(GO) fmt ./...
|
||||
|
||||
# Run go vet
|
||||
vet:
|
||||
@echo "Running go vet..."
|
||||
$(GO) vet ./...
|
||||
|
||||
# Run linters
|
||||
lint: fmt vet
|
||||
@echo "Running linters..."
|
||||
@if command -v golangci-lint >/dev/null 2>&1; then \
|
||||
golangci-lint run; \
|
||||
else \
|
||||
echo "golangci-lint not installed. Run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
|
||||
fi
|
||||
|
||||
# Tidy dependencies
|
||||
tidy:
|
||||
@echo "Tidying dependencies..."
|
||||
$(GO) mod tidy
|
||||
|
||||
# Download dependencies
|
||||
deps:
|
||||
@echo "Downloading dependencies..."
|
||||
$(GO) mod download
|
||||
|
||||
# Help
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " all - Run tests and build"
|
||||
@echo " build - Build the CLI binary"
|
||||
@echo " build-all - Build for multiple platforms"
|
||||
@echo " install - Install the binary"
|
||||
@echo " test - Run tests"
|
||||
@echo " coverage - Run tests with coverage report"
|
||||
@echo " demo - Run the demo"
|
||||
@echo " run - Build and run the CLI"
|
||||
@echo " clean - Clean build artifacts"
|
||||
@echo " fmt - Format code"
|
||||
@echo " vet - Run go vet"
|
||||
@echo " lint - Run linters"
|
||||
@echo " tidy - Tidy dependencies"
|
||||
@echo " deps - Download dependencies"
|
||||
@echo " help - Show this help message"
|
||||
220
README.md
220
README.md
|
|
@ -1,2 +1,220 @@
|
|||
# Mining
|
||||
GoLang Miner management with restful control
|
||||
|
||||
[](https://golang.org)
|
||||
[](https://pkg.go.dev/github.com/Snider/Mining)
|
||||
[](https://goreportcard.com/report/github.com/Snider/Mining)
|
||||
[](https://opensource.org/license/eupl-1-2)
|
||||
[](https://github.com/Snider/Mining/releases)
|
||||
[](https://codecov.io/gh/Snider/Mining)
|
||||
|
||||
GoLang Miner management with RESTful control - A modern, modular package for managing cryptocurrency miners.
|
||||
|
||||
## Overview
|
||||
|
||||
Mining is a Go package designed to provide comprehensive miner management capabilities. It can be used both as a standalone CLI tool and as a module/plugin in other Go projects. The package offers:
|
||||
|
||||
- **Miner Lifecycle Management**: Start, stop, and monitor miners
|
||||
- **Status Tracking**: Real-time status and hash rate monitoring
|
||||
- **CLI Interface**: Easy-to-use command-line interface built with Cobra
|
||||
- **Modular Design**: Import as a package in your own projects
|
||||
- **RESTful Ready**: Designed for integration with RESTful control systems
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Start and stop miners programmatically
|
||||
- ✅ Monitor miner status and performance
|
||||
- ✅ Track hash rates
|
||||
- ✅ List all active miners
|
||||
- ✅ CLI for easy management
|
||||
- ✅ Designed as a reusable Go module
|
||||
- ✅ Comprehensive test coverage
|
||||
- ✅ Standards-compliant configuration (CodeRabbit, GoReleaser)
|
||||
|
||||
## Installation
|
||||
|
||||
### As a CLI Tool
|
||||
|
||||
```bash
|
||||
go install github.com/Snider/Mining/cmd/mining@latest
|
||||
```
|
||||
|
||||
### As a Go Module
|
||||
|
||||
```bash
|
||||
go get github.com/Snider/Mining
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Start a miner
|
||||
mining start --name bitcoin-miner-1 --algorithm sha256 --pool pool.bitcoin.com
|
||||
|
||||
# List all miners
|
||||
mining list
|
||||
|
||||
# Get miner status
|
||||
mining status <miner-id>
|
||||
|
||||
# Stop a miner
|
||||
mining stop <miner-id>
|
||||
|
||||
# Show version
|
||||
mining --version
|
||||
|
||||
# Show help
|
||||
mining --help
|
||||
```
|
||||
|
||||
### As a Go Package
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Snider/Mining/pkg/mining"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a manager
|
||||
manager := mining.NewManager()
|
||||
|
||||
// Start a miner
|
||||
config := mining.MinerConfig{
|
||||
Name: "my-miner",
|
||||
Algorithm: "sha256",
|
||||
Pool: "pool.example.com",
|
||||
Wallet: "your-wallet-address",
|
||||
}
|
||||
|
||||
miner, err := manager.StartMiner(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Started miner: %s\n", miner.ID)
|
||||
|
||||
// Update hash rate
|
||||
manager.UpdateHashRate(miner.ID, 150.5)
|
||||
|
||||
// List all miners
|
||||
miners := manager.ListMiners()
|
||||
for _, m := range miners {
|
||||
fmt.Printf("%s: %s (%.2f H/s)\n", m.ID, m.Name, m.HashRate)
|
||||
}
|
||||
|
||||
// Stop the miner
|
||||
manager.StopMiner(miner.ID)
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.24 or higher
|
||||
- Make (optional, for using Makefile targets)
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Build the CLI
|
||||
go build -o mining ./cmd/mining
|
||||
|
||||
# Run demo
|
||||
go run main.go
|
||||
|
||||
# Run tests
|
||||
go test ./...
|
||||
|
||||
# Run tests with coverage
|
||||
go test -cover ./...
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── cmd/
|
||||
│ └── mining/ # CLI application
|
||||
│ ├── main.go # CLI entry point
|
||||
│ └── cmd/ # Cobra commands
|
||||
├── pkg/
|
||||
│ └── mining/ # Core mining package
|
||||
│ ├── mining.go # Main package code
|
||||
│ └── mining_test.go
|
||||
├── main.go # Demo/development main
|
||||
├── .coderabbit.yaml # CodeRabbit configuration
|
||||
├── .goreleaser.yaml # GoReleaser configuration
|
||||
├── .gitignore
|
||||
├── go.mod
|
||||
├── LICENSE
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### CodeRabbit
|
||||
|
||||
The project uses CodeRabbit for automated code reviews. Configuration is in `.coderabbit.yaml`.
|
||||
|
||||
### GoReleaser
|
||||
|
||||
Releases are managed with GoReleaser. Configuration is in `.goreleaser.yaml`. To create a release:
|
||||
|
||||
```bash
|
||||
# Tag a version
|
||||
git tag -a v0.1.0 -m "Release v0.1.0"
|
||||
git push origin v0.1.0
|
||||
|
||||
# GoReleaser will automatically build and publish
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Types
|
||||
|
||||
#### `Manager`
|
||||
Main manager for miner operations.
|
||||
|
||||
#### `Miner`
|
||||
Represents a mining instance with ID, name, status, and performance metrics.
|
||||
|
||||
#### `MinerConfig`
|
||||
Configuration for starting a new miner.
|
||||
|
||||
### Functions
|
||||
|
||||
- `NewManager() *Manager` - Create a new miner manager
|
||||
- `StartMiner(config MinerConfig) (*Miner, error)` - Start a new miner
|
||||
- `StopMiner(id string) error` - Stop a running miner
|
||||
- `GetMiner(id string) (*Miner, error)` - Get miner by ID
|
||||
- `ListMiners() []*Miner` - List all miners
|
||||
- `UpdateHashRate(id string, hashRate float64) error` - Update miner hash rate
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
1. Fork the repository
|
||||
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the EUPL-1.2 License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Built with [Cobra](https://github.com/spf13/cobra) for CLI functionality
|
||||
- Configured for [CodeRabbit](https://coderabbit.ai) automated reviews
|
||||
- Releases managed with [GoReleaser](https://goreleaser.com)
|
||||
|
||||
## Support
|
||||
|
||||
For issues, questions, or contributions, please open an issue on GitHub.
|
||||
|
|
|
|||
38
cmd/mining/cmd/list.go
Normal file
38
cmd/mining/cmd/list.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// listCmd represents the list command
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all miners",
|
||||
Long: `List all miners and their current status.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
miners := getManager().ListMiners()
|
||||
|
||||
if len(miners) == 0 {
|
||||
fmt.Println("No miners found")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%-20s %-20s %-10s %-12s\n", "ID", "Name", "Status", "Hash Rate")
|
||||
fmt.Println("--------------------------------------------------------------------------------")
|
||||
for _, miner := range miners {
|
||||
fmt.Printf("%-20s %-20s %-10s %-12.2f\n",
|
||||
miner.ID,
|
||||
miner.Name,
|
||||
miner.Status,
|
||||
miner.HashRate,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
44
cmd/mining/cmd/root.go
Normal file
44
cmd/mining/cmd/root.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/Snider/Mining/pkg/mining"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
manager *mining.Manager
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "mining",
|
||||
Short: "Mining CLI - Manage miners with RESTful control",
|
||||
Long: `Mining is a CLI tool for managing cryptocurrency miners.
|
||||
It provides commands to start, stop, list, and manage miners with RESTful control capabilities.`,
|
||||
Version: mining.GetVersion(),
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initManager)
|
||||
}
|
||||
|
||||
// initManager initializes the miner manager
|
||||
func initManager() {
|
||||
if manager == nil {
|
||||
manager = mining.NewManager()
|
||||
}
|
||||
}
|
||||
|
||||
// getManager returns the singleton manager instance
|
||||
func getManager() *mining.Manager {
|
||||
if manager == nil {
|
||||
manager = mining.NewManager()
|
||||
}
|
||||
return manager
|
||||
}
|
||||
50
cmd/mining/cmd/start.go
Normal file
50
cmd/mining/cmd/start.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Snider/Mining/pkg/mining"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
minerName string
|
||||
minerAlgorithm string
|
||||
minerPool string
|
||||
minerWallet string
|
||||
)
|
||||
|
||||
// startCmd represents the start command
|
||||
var startCmd = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start a new miner",
|
||||
Long: `Start a new miner with the specified configuration.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := mining.MinerConfig{
|
||||
Name: minerName,
|
||||
Algorithm: minerAlgorithm,
|
||||
Pool: minerPool,
|
||||
Wallet: minerWallet,
|
||||
}
|
||||
|
||||
miner, err := getManager().StartMiner(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start miner: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Miner started successfully:\n")
|
||||
fmt.Printf(" ID: %s\n", miner.ID)
|
||||
fmt.Printf(" Name: %s\n", miner.Name)
|
||||
fmt.Printf(" Status: %s\n", miner.Status)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(startCmd)
|
||||
startCmd.Flags().StringVarP(&minerName, "name", "n", "", "Miner name (required)")
|
||||
startCmd.Flags().StringVarP(&minerAlgorithm, "algorithm", "a", "sha256", "Mining algorithm")
|
||||
startCmd.Flags().StringVarP(&minerPool, "pool", "p", "", "Mining pool address")
|
||||
startCmd.Flags().StringVarP(&minerWallet, "wallet", "w", "", "Wallet address")
|
||||
startCmd.MarkFlagRequired("name")
|
||||
}
|
||||
35
cmd/mining/cmd/status.go
Normal file
35
cmd/mining/cmd/status.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// statusCmd represents the status command
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status [miner-id]",
|
||||
Short: "Get status of a miner",
|
||||
Long: `Get detailed status information for a specific miner.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
minerID := args[0]
|
||||
|
||||
miner, err := getManager().GetMiner(minerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get miner: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Miner Status:\n")
|
||||
fmt.Printf(" ID: %s\n", miner.ID)
|
||||
fmt.Printf(" Name: %s\n", miner.Name)
|
||||
fmt.Printf(" Status: %s\n", miner.Status)
|
||||
fmt.Printf(" Start Time: %s\n", miner.StartTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf(" Hash Rate: %.2f H/s\n", miner.HashRate)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(statusCmd)
|
||||
}
|
||||
30
cmd/mining/cmd/stop.go
Normal file
30
cmd/mining/cmd/stop.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// stopCmd represents the stop command
|
||||
var stopCmd = &cobra.Command{
|
||||
Use: "stop [miner-id]",
|
||||
Short: "Stop a running miner",
|
||||
Long: `Stop a running miner by its ID.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
minerID := args[0]
|
||||
|
||||
err := getManager().StopMiner(minerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop miner: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Miner %s stopped successfully\n", minerID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(stopCmd)
|
||||
}
|
||||
15
cmd/mining/main.go
Normal file
15
cmd/mining/main.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Snider/Mining/cmd/mining/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
10
go.mod
Normal file
10
go.mod
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
module github.com/Snider/Mining
|
||||
|
||||
go 1.24
|
||||
|
||||
require github.com/spf13/cobra v1.8.1
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
)
|
||||
10
go.sum
Normal file
10
go.sum
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
102
main.go
Normal file
102
main.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Demo main.go for development and testing
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/Snider/Mining/pkg/mining"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Mining Package Demo")
|
||||
fmt.Println("===================")
|
||||
fmt.Println()
|
||||
|
||||
// Create a new manager
|
||||
manager := mining.NewManager()
|
||||
fmt.Println("✓ Created new mining manager")
|
||||
|
||||
// Start a few miners
|
||||
configs := []mining.MinerConfig{
|
||||
{
|
||||
Name: "bitcoin-miner-1",
|
||||
Algorithm: "sha256",
|
||||
Pool: "pool.bitcoin.com",
|
||||
Wallet: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
|
||||
},
|
||||
{
|
||||
Name: "ethereum-miner-1",
|
||||
Algorithm: "ethash",
|
||||
Pool: "pool.ethereum.org",
|
||||
Wallet: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
|
||||
},
|
||||
}
|
||||
|
||||
var minerIDs []string
|
||||
for _, config := range configs {
|
||||
miner, err := manager.StartMiner(config)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start miner: %v", err)
|
||||
}
|
||||
minerIDs = append(minerIDs, miner.ID)
|
||||
fmt.Printf("✓ Started miner: %s (ID: %s)\n", miner.Name, miner.ID)
|
||||
time.Sleep(10 * time.Millisecond) // Small delay for unique IDs
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
// Update hash rates
|
||||
hashRates := []float64{150.5, 320.75}
|
||||
for i, id := range minerIDs {
|
||||
err := manager.UpdateHashRate(id, hashRates[i])
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to update hash rate: %v", err)
|
||||
}
|
||||
fmt.Printf("✓ Updated hash rate for %s: %.2f H/s\n", id, hashRates[i])
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
// List all miners
|
||||
fmt.Println("Active Miners:")
|
||||
fmt.Println("--------------")
|
||||
miners := manager.ListMiners()
|
||||
for _, miner := range miners {
|
||||
fmt.Printf(" %s: %s (%.2f H/s, %s)\n",
|
||||
miner.ID,
|
||||
miner.Name,
|
||||
miner.HashRate,
|
||||
miner.Status,
|
||||
)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
// Get specific miner status
|
||||
if len(minerIDs) > 0 {
|
||||
miner, err := manager.GetMiner(minerIDs[0])
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get miner: %v", err)
|
||||
}
|
||||
fmt.Printf("Detailed Status for %s:\n", miner.Name)
|
||||
fmt.Printf(" ID: %s\n", miner.ID)
|
||||
fmt.Printf(" Status: %s\n", miner.Status)
|
||||
fmt.Printf(" Start Time: %s\n", miner.StartTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf(" Hash Rate: %.2f H/s\n", miner.HashRate)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Stop a miner
|
||||
if len(minerIDs) > 0 {
|
||||
err := manager.StopMiner(minerIDs[0])
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to stop miner: %v", err)
|
||||
}
|
||||
fmt.Printf("✓ Stopped miner: %s\n", minerIDs[0])
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Demo completed successfully! Version: %s\n", mining.GetVersion())
|
||||
}
|
||||
104
pkg/mining/mining.go
Normal file
104
pkg/mining/mining.go
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Package mining provides core functionality for miner management
|
||||
package mining
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Miner represents a mining instance
|
||||
type Miner struct {
|
||||
ID string
|
||||
Name string
|
||||
Status string
|
||||
StartTime time.Time
|
||||
HashRate float64
|
||||
}
|
||||
|
||||
// MinerConfig holds configuration for a miner
|
||||
type MinerConfig struct {
|
||||
Name string
|
||||
Algorithm string
|
||||
Pool string
|
||||
Wallet string
|
||||
}
|
||||
|
||||
// Manager handles miner lifecycle and operations
|
||||
type Manager struct {
|
||||
miners map[string]*Miner
|
||||
}
|
||||
|
||||
// NewManager creates a new miner manager
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
miners: make(map[string]*Miner),
|
||||
}
|
||||
}
|
||||
|
||||
// StartMiner starts a new miner with the given configuration
|
||||
func (m *Manager) StartMiner(config MinerConfig) (*Miner, error) {
|
||||
if config.Name == "" {
|
||||
return nil, fmt.Errorf("miner name is required")
|
||||
}
|
||||
|
||||
miner := &Miner{
|
||||
ID: generateID(),
|
||||
Name: config.Name,
|
||||
Status: "running",
|
||||
StartTime: time.Now(),
|
||||
HashRate: 0.0,
|
||||
}
|
||||
|
||||
m.miners[miner.ID] = miner
|
||||
return miner, nil
|
||||
}
|
||||
|
||||
// StopMiner stops a running miner
|
||||
func (m *Manager) StopMiner(id string) error {
|
||||
miner, exists := m.miners[id]
|
||||
if !exists {
|
||||
return fmt.Errorf("miner not found: %s", id)
|
||||
}
|
||||
|
||||
miner.Status = "stopped"
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMiner retrieves a miner by ID
|
||||
func (m *Manager) GetMiner(id string) (*Miner, error) {
|
||||
miner, exists := m.miners[id]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("miner not found: %s", id)
|
||||
}
|
||||
return miner, nil
|
||||
}
|
||||
|
||||
// ListMiners returns all miners
|
||||
func (m *Manager) ListMiners() []*Miner {
|
||||
miners := make([]*Miner, 0, len(m.miners))
|
||||
for _, miner := range m.miners {
|
||||
miners = append(miners, miner)
|
||||
}
|
||||
return miners
|
||||
}
|
||||
|
||||
// UpdateHashRate updates the hash rate for a miner
|
||||
func (m *Manager) UpdateHashRate(id string, hashRate float64) error {
|
||||
miner, exists := m.miners[id]
|
||||
if !exists {
|
||||
return fmt.Errorf("miner not found: %s", id)
|
||||
}
|
||||
|
||||
miner.HashRate = hashRate
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateID generates a unique ID for a miner
|
||||
func generateID() string {
|
||||
return fmt.Sprintf("miner-%d", time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// GetVersion returns the package version
|
||||
func GetVersion() string {
|
||||
return "0.1.0"
|
||||
}
|
||||
154
pkg/mining/mining_test.go
Normal file
154
pkg/mining/mining_test.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package mining
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
manager := NewManager()
|
||||
if manager == nil {
|
||||
t.Fatal("NewManager returned nil")
|
||||
}
|
||||
if manager.miners == nil {
|
||||
t.Error("Manager miners map is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
config := MinerConfig{
|
||||
Name: "test-miner",
|
||||
Algorithm: "sha256",
|
||||
Pool: "pool.example.com",
|
||||
Wallet: "wallet123",
|
||||
}
|
||||
|
||||
miner, err := manager.StartMiner(config)
|
||||
if err != nil {
|
||||
t.Fatalf("StartMiner failed: %v", err)
|
||||
}
|
||||
|
||||
if miner.Name != config.Name {
|
||||
t.Errorf("Expected name %s, got %s", config.Name, miner.Name)
|
||||
}
|
||||
if miner.Status != "running" {
|
||||
t.Errorf("Expected status 'running', got %s", miner.Status)
|
||||
}
|
||||
if miner.ID == "" {
|
||||
t.Error("Miner ID is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartMinerWithoutName(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
config := MinerConfig{
|
||||
Algorithm: "sha256",
|
||||
}
|
||||
|
||||
_, err := manager.StartMiner(config)
|
||||
if err == nil {
|
||||
t.Error("Expected error for miner without name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
config := MinerConfig{Name: "test-miner"}
|
||||
miner, _ := manager.StartMiner(config)
|
||||
|
||||
err := manager.StopMiner(miner.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("StopMiner failed: %v", err)
|
||||
}
|
||||
|
||||
if miner.Status != "stopped" {
|
||||
t.Errorf("Expected status 'stopped', got %s", miner.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopNonExistentMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
err := manager.StopMiner("non-existent")
|
||||
if err == nil {
|
||||
t.Error("Expected error for stopping non-existent miner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
config := MinerConfig{Name: "test-miner"}
|
||||
startedMiner, _ := manager.StartMiner(config)
|
||||
|
||||
retrievedMiner, err := manager.GetMiner(startedMiner.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetMiner failed: %v", err)
|
||||
}
|
||||
|
||||
if retrievedMiner.ID != startedMiner.ID {
|
||||
t.Errorf("Expected ID %s, got %s", startedMiner.ID, retrievedMiner.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNonExistentMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
_, err := manager.GetMiner("non-existent")
|
||||
if err == nil {
|
||||
t.Error("Expected error for getting non-existent miner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListMiners(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
// Start multiple miners
|
||||
for i := 0; i < 3; i++ {
|
||||
config := MinerConfig{Name: "test-miner"}
|
||||
_, _ = manager.StartMiner(config)
|
||||
time.Sleep(time.Millisecond) // Ensure unique IDs
|
||||
}
|
||||
|
||||
miners := manager.ListMiners()
|
||||
if len(miners) != 3 {
|
||||
t.Errorf("Expected 3 miners, got %d", len(miners))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateHashRate(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
config := MinerConfig{Name: "test-miner"}
|
||||
miner, _ := manager.StartMiner(config)
|
||||
|
||||
newHashRate := 123.45
|
||||
err := manager.UpdateHashRate(miner.ID, newHashRate)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateHashRate failed: %v", err)
|
||||
}
|
||||
|
||||
if miner.HashRate != newHashRate {
|
||||
t.Errorf("Expected hash rate %f, got %f", newHashRate, miner.HashRate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateHashRateNonExistent(t *testing.T) {
|
||||
manager := NewManager()
|
||||
|
||||
err := manager.UpdateHashRate("non-existent", 100.0)
|
||||
if err == nil {
|
||||
t.Error("Expected error for updating non-existent miner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
version := GetVersion()
|
||||
if version == "" {
|
||||
t.Error("Version is empty")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue