Skip to content

Commit 63a9769

Browse files
committed
cleaning documentation
1 parent b4a7bb7 commit 63a9769

5 files changed

Lines changed: 258 additions & 38 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
- name: Set up Go
3939
uses: actions/setup-go@v5
4040
with:
41-
go-version: '1.21'
41+
go-version: '1.22'
4242
check-latest: true
4343

4444
- name: Build binary

CONTRIBUTING.md

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ Thank you for your interest in contributing to `neoctl`!
1717

1818
## Building the Project
1919

20-
We use a `Makefile` to automate the build process and inject version information.
20+
We use a ```Makefile``` to automate the build process and inject version information.
2121

2222
### Standard Build
2323

24-
To build the `neoctl` binary:
24+
To build the ```neoctl``` binary:
2525

2626
```bash
2727
make build
@@ -31,7 +31,7 @@ This ensures that the project builds correctly and that version information (com
3131

3232
### Build with Custom Version
3333

34-
To override the version (default is `v0.0.1`):
34+
To override the version (default is ```v0.0.1```):
3535

3636
```bash
3737
make build VERSION=1.2.3
@@ -46,7 +46,6 @@ make verify
4646
# OR manually
4747
./neoctl verify
4848
```
49-
5049
To check the version information:
5150

5251
```bash
@@ -55,22 +54,55 @@ make version
5554
./neoctl version
5655
```
5756

57+
## Testing
58+
59+
All new features and significant bug fixes must include unit tests.
60+
61+
### Running Tests
62+
63+
To run the full test suite:
64+
65+
```bash
66+
make test
67+
```
68+
69+
Please ensure all tests pass before submitting a pull request.
70+
71+
### Example
72+
73+
Here is an example of a simple unit test:
74+
75+
```go
76+
package utils
77+
78+
import "testing"
79+
80+
func TestAdd(t *testing.T) {
81+
result := Add(2, 3)
82+
expected := 5
83+
if result != expected {
84+
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
85+
}
86+
}
87+
```
88+
89+
5890
## Project Structure
5991
60-
- `cmd/neoctl`: Main entry point for the CLI.
61-
- `internal`: Internal library code (runtime checks, version info, etc.).
92+
- ```cmd/neoctl```: Main entry point for the CLI.
93+
- ```internal```: Internal library code (runtime checks, version info, etc.).
6294
6395
## Release Process
6496
6597
We use GitHub Actions to automate the release process.
6698
67-
1. **Tagging**: Releases are triggered by pushing a tag starting with `v` (e.g., `v1.0.0`, `v1.2.3-rc1`).
68-
2. **Automation**: The `.github/workflows/release.yml` workflow automatically runs on tag push.
99+
1. **Tagging**: Releases are triggered by pushing a tag starting with ```v``` (e.g., ```v1.0.0```, ```v1.2.3-rc1```).
100+
2. **Automation**: The ```release.yml``` workflow automatically runs on tag push.
69101
3. **Cross-Compilation**: The workflow builds binaries for:
70102
- Linux (amd64, arm64)
71103
- macOS (amd64, arm64)
72104
- Windows (amd64)
73-
4. **Artifacts**: The binaries are compressed (`.tar.gz` for Unix, `.zip` for Windows) and attached to a new GitHub Release.
105+
4. **Artifacts**: The binaries are compressed (```tar.gz``` for Unix, ```zip``` for Windows) and attached to a new GitHub Release.
74106
5. **Versioning**: The build version matches the git tag.
75107
76108
To trigger a release:

README.md

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# NetApp Neo CLI (`neoctl`)
22

3-
`neoctl` is a command-line tool designed to simplify the deployment and management of NetApp Neo instances. It handles configuration, prerequisites verification, and lifecycle management (start, stop, delete) for Neo Core, Neo UI, and the associated database.
3+
`neoctl` is a command-line tool designed to simplify the deployment and management of NetApp Neo instances. It handles configuration, prerequisites verification, and lifecycle management (configure, start, stop, delete) for Neo Core, Neo UI, and the associated database.
44

55
## Features
66

@@ -13,43 +13,47 @@
1313

1414
## Installation
1515

16+
### Download Binary
17+
18+
Download the latest release for your operating system from the [Releases](https://github.com/netapp/neoctl/releases) page.
19+
20+
#### Linux / macOS
21+
1. Download the archive for your architecture (e.g., ```neoctl-linux-amd64.tar.gz``` or ```neoctl-darwin-arm64.tar.gz```).
22+
2. Extract the binary:
23+
```bash
24+
tar -xzvf neoctl-*.tar.gz
25+
```
26+
3. Move the binary to a directory in your ```PATH```:
27+
```bash
28+
sudo mv neoctl /usr/local/bin/
29+
```
30+
31+
#### Windows
32+
1. Download the zip file (e.g., ```neoctl-windows-amd64.zip```).
33+
2. Extract the ```neoctl.exe``` binary.
34+
3. Add the directory containing ```neoctl.exe``` to your system ```PATH``` environment variable.
35+
1636
### Build from Source
1737

1838
Requirements:
19-
- Go 1.21+
39+
- Go 1.22+
2040
- Make
2141

2242
```bash
23-
git clone <repository-url>
24-
cd neo-cli
43+
git clone https://github.com/netapp/neoctl.git
44+
cd neoctl
2545
make build
2646
```
27-
This produces the `neoctl` binary in the current directory.
28-
29-
## Quick Start
47+
This produces the ```neoctl``` binary in the current directory.
3048

31-
1. **Verify Prerequisites**:
32-
```bash
33-
./neoctl verify
34-
```
49+
## The Fast track
3550

36-
2. **Configure Bundle Versions** (fetches latest):
37-
```bash
38-
./neoctl bundle get
39-
```
40-
41-
3. **Start Default Instance**:
42-
```bash
43-
./neoctl instance configure default
44-
./neoctl instance start neo-default
45-
```
46-
47-
Access the Neo Console at `http://localhost:8080`.
48-
49-
4. **Stop Instance**:
50-
```bash
51-
./neoctl instance stop neo-default
52-
```
51+
```bash
52+
./neoctl instance configure neotest
53+
./neoctl instance start neotest
54+
```
55+
**That's it!!!**
56+
Access the Neo Console at ```http://localhost:8080``` or ```http://your_ip:8081```.
5357

5458
## Documentation
5559

internal/runner/compose_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2026 NetApp, Inc. All Rights Reserved.
2+
package runner
3+
4+
import (
5+
"testing"
6+
)
7+
8+
func TestCheckInstanceRunning(t *testing.T) {
9+
instanceName := "test-neo"
10+
11+
tests := []struct {
12+
name string
13+
statusOutput string
14+
expectedRunning bool
15+
expectedRunCnt int
16+
expectedTotCnt int
17+
}{
18+
{
19+
name: "All Running (Docker style)",
20+
statusOutput: `
21+
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
22+
test-neo-neo neo:latest "/entrypoint..." neo 2 minutes ago Up 2 minutes 8080/tcp
23+
test-neo-neoui neoui:latest "/entrypoint..." neoui 2 minutes ago Up 2 minutes 80/tcp
24+
`,
25+
expectedRunning: true,
26+
expectedRunCnt: 2,
27+
expectedTotCnt: 2,
28+
},
29+
{
30+
name: "Partially Running",
31+
statusOutput: `
32+
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
33+
test-neo-neo neo:latest "/entrypoint..." neo 2 minutes ago Up 2 minutes 8080/tcp
34+
test-neo-neoui neoui:latest "/entrypoint..." neoui 2 minutes ago Exited (1) 80/tcp
35+
`,
36+
expectedRunning: true, // At least one is running
37+
expectedRunCnt: 1,
38+
expectedTotCnt: 2,
39+
},
40+
{
41+
name: "All Stopped",
42+
statusOutput: `
43+
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
44+
test-neo-neo neo:latest "/entrypoint..." neo 2 minutes ago Exited (137)
45+
test-neo-neoui neoui:latest "/entrypoint..." neoui 2 minutes ago Exited (0)
46+
`,
47+
expectedRunning: false,
48+
expectedRunCnt: 0,
49+
expectedTotCnt: 2,
50+
},
51+
{
52+
name: "Podman style output",
53+
statusOutput: `
54+
Container ID Image Command Created Status Ports Names
55+
a1b2c3d4e5f6 localhost/neo:latest /bin/sh -c /app... 5 minutes ago Up 5 minutes 0.0.0.0:8081->8080/tcp test-neo-neo
56+
f6e5d4c3b2a1 localhost/neoui:latest /docker-entrypo... 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp test-neo-neoui
57+
`,
58+
expectedRunning: true,
59+
expectedRunCnt: 2,
60+
expectedTotCnt: 2,
61+
},
62+
{
63+
name: "Empty Output",
64+
statusOutput: "",
65+
expectedRunning: false,
66+
expectedRunCnt: 0,
67+
expectedTotCnt: 0,
68+
},
69+
}
70+
71+
for _, tt := range tests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
isRunning, runCnt, totCnt := CheckInstanceRunning(tt.statusOutput, instanceName)
74+
75+
if isRunning != tt.expectedRunning {
76+
t.Errorf("CheckInstanceRunning() isRunning = %v, want %v", isRunning, tt.expectedRunning)
77+
}
78+
if runCnt != tt.expectedRunCnt {
79+
t.Errorf("CheckInstanceRunning() runCnt = %v, want %v", runCnt, tt.expectedRunCnt)
80+
}
81+
if totCnt != tt.expectedTotCnt {
82+
t.Errorf("CheckInstanceRunning() totCnt = %v, want %v", totCnt, tt.expectedTotCnt)
83+
}
84+
})
85+
}
86+
}

internal/utils/archive_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2026 NetApp, Inc. All Rights Reserved.
2+
package utils
3+
4+
import (
5+
"archive/tar"
6+
"compress/gzip"
7+
"io"
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
)
12+
13+
func TestCreateTarGz(t *testing.T) {
14+
// 1. Setup temporary source directory with files
15+
srcDir, err := os.MkdirTemp("", "archive_test_src")
16+
if err != nil {
17+
t.Fatalf("Failed to create temp src dir: %v", err)
18+
}
19+
defer os.RemoveAll(srcDir)
20+
21+
files := map[string]string{
22+
"file1.txt": "Content of file 1",
23+
"subdir/file2.go": "package main\nfunc main() {}",
24+
}
25+
26+
for path, content := range files {
27+
fullPath := filepath.Join(srcDir, path)
28+
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
29+
t.Fatalf("Failed to create subdir for %s: %v", path, err)
30+
}
31+
if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
32+
t.Fatalf("Failed to write file %s: %v", path, err)
33+
}
34+
}
35+
36+
// 2. Define destination archive path
37+
tmpDest, err := os.MkdirTemp("", "archive_test_dest")
38+
if err != nil {
39+
t.Fatalf("Failed to create temp dest dir: %v", err)
40+
}
41+
defer os.RemoveAll(tmpDest)
42+
43+
destFile := filepath.Join(tmpDest, "archive.tar.gz")
44+
45+
// 3. Run CreateTarGz
46+
if err := CreateTarGz(srcDir, destFile); err != nil {
47+
t.Fatalf("CreateTarGz failed: %v", err)
48+
}
49+
50+
// 4. Verify Archive Contents
51+
f, err := os.Open(destFile)
52+
if err != nil {
53+
t.Fatalf("Failed to open archive: %v", err)
54+
}
55+
defer f.Close()
56+
57+
gzr, err := gzip.NewReader(f)
58+
if err != nil {
59+
t.Fatalf("Failed to create gzip reader: %v", err)
60+
}
61+
defer gzr.Close()
62+
63+
tr := tar.NewReader(gzr)
64+
65+
foundFiles := make(map[string]bool)
66+
for {
67+
header, err := tr.Next()
68+
if err == io.EOF {
69+
break
70+
}
71+
if err != nil {
72+
t.Fatalf("Tar iterator error: %v", err)
73+
}
74+
75+
// Check if file is in our expected list
76+
// Relative paths in archive might differ slightly (e.g. ./file1.txt vs file1.txt)
77+
// Our implementation uses filepath.Rel which typically produces clean relative paths
78+
if _, ok := files[header.Name]; ok {
79+
foundFiles[header.Name] = true
80+
81+
// Verify content
82+
content, err := io.ReadAll(tr)
83+
if err != nil {
84+
t.Fatalf("Failed to read content for %s: %v", header.Name, err)
85+
}
86+
if string(content) != files[header.Name] {
87+
t.Errorf("Content mismatch for %s. Got %s, want %s", header.Name, string(content), files[header.Name])
88+
}
89+
}
90+
}
91+
92+
// Verify all expected files were found
93+
for file := range files {
94+
if !foundFiles[file] {
95+
t.Errorf("File %s not found in archive", file)
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)