{"owner":"semaphoreui","repo":"semaphore","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":[".claude/CLAUDE.md",".github/copilot-instructions.md"],"skills":{".claude/CLAUDE.md":"# Claude Code Instructions\n\n## Writing Plans\n\nAll plans, tasks, researches for AI agents stored in folder AGENTS.\n\nEach plan has markdown-format and stored in folder AGENTS/plans/<version>.\n\nPlan can be split to tasks. Each task describes in details how to implement some part of some plan.\n\n## Code Style\n\n1. Do not use global variables. Global variables are forbidden.\n\n## High Availability Support\n\nAll solutions must work in High Availability mode.\n\n## Security Is the #1 Priority\n\nAll solutions must be secure by design. Do not consider any solution that introduces security risks.\n\n## How to do research\n\nUse MCP server `research` if your asks you to research something.\n\n## Writing Tests\n\nUse `github.com/stretchr/testify/assert` (and `require` when a failure should stop the test immediately).\n\n### Run tests\n\n```bash\ngo test ./path/to/package/ -run TestFunctionName -v -count=1\n```\n\n### Rules\n\n- Test file goes next to the source: `foo.go` → `foo_test.go`, same package.\n- Use `assert.Equal`, `assert.Empty`, `assert.Nil`, `assert.True`, `assert.Panics`, etc. Never use raw `if`/`t.Fatalf` for assertions.\n- Use `require` (instead of `assert`) when subsequent lines depend on the check passing (e.g., `require.NoError` before using a result).\n- Use table-driven tests with `t.Run` for multiple inputs to the same function.\n- Use `t.TempDir()` for temporary files — cleanup is automatic.\n- When testing code that uses package-level globals (e.g., `util.Config`), initialize them in a helper and reset state between tests.\n- When testing code that reads `os.Stdin`, swap it with `os.Pipe()` and restore in `defer`.\n\n### Template\n\n```go\npackage mypkg\n\nimport (\n    \"testing\"\n\n    \"github.com/stretchr/testify/assert\"\n    \"github.com/stretchr/testify/require\"\n)\n\nfunc TestMyFunction(t *testing.T) {\n    // setup\n    input := \"value\"\n\n    // act\n    result, err := MyFunction(input)\n\n    // assert\n    require.NoError(t, err)\n    assert.Equal(t, \"expected\", result)\n}\n\nfunc TestMyFunction_TableDriven(t *testing.T) {\n    tests := []struct {\n        name     string\n        input    string\n        expected string\n    }{\n        {\"short input\", \"abc\", \"ABC\"},\n        {\"empty input\", \"\", \"\"},\n    }\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            assert.Equal(t, tt.expected, MyFunction(tt.input))\n        })\n    }\n}\n\nfunc TestMyFunction_Panics(t *testing.T) {\n    assert.Panics(t, func() {\n        MyFunction(nil)\n    })\n}\n```\n\n### Common assertions\n\n| Assertion | Use when |\n|-----------|----------|\n| `assert.Equal(t, expected, actual)` | Comparing values |\n| `assert.NotEqual(t, a, b)` | Values must differ |\n| `assert.Nil(t, val)` / `assert.NotNil(t, val)` | Pointer/interface checks |\n| `assert.Empty(t, val)` | String, slice, or map is zero-length |\n| `assert.Contains(t, str, substr)` | Substring or element in collection |\n| `assert.True(t, cond)` / `assert.False(t, cond)` | Boolean conditions |\n| `assert.NoError(t, err)` / `assert.Error(t, err)` | Error checks |\n| `assert.ErrorContains(t, err, \"msg\")` | Error with specific message |\n| `assert.Panics(t, func(){...})` | Code must panic |\n| `require.NoError(t, err)` | Stop test immediately on error |\n\n### Initializing `util.Config` in tests\n\nMany packages depend on `util.Config` which is a `*ConfigType` pointer (nil by default). Initialize before use:\n\n```go\nfunc setupConfig() {\n    if util.Config == nil {\n        util.Config = &util.ConfigType{}\n    }\n    // Initialize nested pointers as needed:\n    if util.Config.Runner == nil {\n        util.Config.Runner = &util.RunnerConfig{}\n    }\n}\n```\n\n### HTTP handler tests\n\nUse `net/http/httptest`:\n\n```go\nfunc TestMyHandler(t *testing.T) {\n    req := httptest.NewRequest(http.MethodGet, \"/api/endpoint\", nil)\n    w := httptest.NewRecorder()\n\n    myHandler(w, req)\n\n    assert.Equal(t, http.StatusOK, w.Code)\n    assert.Contains(t, w.Body.String(), \"expected\")\n}\n```\n",".github/copilot-instructions.md":"# Semaphore UI Development Instructions\n\n**Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**\n\nSemaphore UI is a modern web interface for managing popular DevOps tools like Ansible, Terraform, PowerShell, and Bash scripts. It's built with Go (backend) and Vue.js (frontend), using Task runner for build automation.\n\n## Working Effectively\n\n### Bootstrap, build, and test the repository:\n- Install Go 1.21+ (currently requires go version 1.21 or higher)\n- Install Node.js 16+ \n- Install Task runner: `go install github.com/go-task/task/v3/cmd/task@latest`\n- Install dependencies: `task deps` -- takes 3 minutes first time (faster with cache). NEVER CANCEL. Set timeout to 5+ minutes.\n- Build the application: `task build` -- takes 1.5 minutes. NEVER CANCEL. Set timeout to 3+ minutes.\n- Run tests: `task test` -- takes 33 seconds. NEVER CANCEL. Set timeout to 2+ minutes.\n\n### Run the application:\n- ALWAYS run the bootstrapping steps first\n- Setup database and admin user: `./bin/semaphore setup` (interactive, use SQLite option 4 for development)\n- Start server: `./bin/semaphore server --config ./config.json`\n- Web UI: http://localhost:3000 (login: admin / changeme)\n- API: http://localhost:3000/api/ (test with: `curl http://localhost:3000/api/ping`)\n\n## Validation\n\n- **CRITICAL**: Always manually validate any new code by building and running the application.\n- ALWAYS run through at least one complete end-to-end scenario after making changes:\n  1. Build the application: `task build`\n  2. Start the server: `./bin/semaphore server --config ./config.json`\n  3. Test API endpoint: `curl http://localhost:3000/api/ping` (should return \"pong\")\n  4. Access web UI at http://localhost:3000 and verify it loads\n  5. For auth changes: Test login with admin/changeme\n- For significant changes, run full setup process to ensure setup still works\n- Always build and exercise your changes before considering the task complete\n\n### Complete Validation Scenario (for major changes):\n```bash\n# 1. Clean build\ntask build\n\n# 2. Setup database (if config.json doesn't exist)\n./bin/semaphore setup  # Choose option 4 (SQLite), use admin/changeme\n\n# 3. Start server\n./bin/semaphore server --config ./config.json\n\n# 4. Test in another terminal\ncurl http://localhost:3000/api/ping  # Should return \"pong\"\ncurl -I http://localhost:3000/       # Should return HTTP 200\n\n# 5. Test web interface manually in browser at http://localhost:3000\n# 6. Test login with admin/changeme if auth-related changes\n```\n\n### Linting and Code Quality\n\n- Frontend linting: `cd web && npm run lint` (has known warnings about console statements and asset sizes - ignore existing issues)\n- Backend linting: `golangci-lint run --timeout=3m` (has known type errors due to module import issues - ignore existing issues) \n- **DO NOT** try to fix existing linting issues unless specifically asked to\n- Always run linting on new code you add to ensure it follows project standards\n- Install golangci-lint if needed: `go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2`\n\n## Common Tasks\n\n### Repository Structure\n```\n.\n├── README.md           - Project documentation  \n├── CONTRIBUTING.md     - Development guidelines\n├── Taskfile.yml       - Task runner configuration\n├── go.mod             - Go module dependencies\n├── web/               - Vue.js frontend application\n│   ├── package.json   - Frontend dependencies\n│   ├── src/           - Vue.js source code\n│   └── public/        - Static assets\n├── cli/               - Go CLI application entry point  \n├── api/               - Go API server endpoints\n├── db/                - Database models and interfaces\n├── services/          - Business logic services\n├── util/              - Utility functions and configuration\n├── bin/               - Built binaries (after build)\n└── config.json       - Runtime configuration (after setup)\n```\n\n### Key Commands Reference\n```bash\n# Install task runner\ngo install github.com/go-task/task/v3/cmd/task@latest\n\n# Install all dependencies (backend + frontend + tools)\ntask deps\n\n# Build application (frontend + backend)\ntask build\n\n# Run tests\ntask test  \n\n# Run linting\ntask lint\n\n# Setup application (interactive)\n./bin/semaphore setup\n\n# Start server\n./bin/semaphore server --config ./config.json\n\n# View available task commands\ntask --list\n```\n\n### Database Options for Development\n\nDuring setup, choose option 4 (SQLite) for the simplest development setup:\n\n- No external database server required\n- Database file stored at the path configured in `config.json` (default under `/tmp/`)\n- Perfect for development and testing\n\n> **Note:** BoltDB (option 2) was removed in Semaphore 2.19. If you have an existing\n> `database.boltdb` file, migrate to SQLite, MySQL, or PostgreSQL before upgrading.\n\n### Frontend Development\n- Vue.js 2.x application in `web/` directory\n- Built with Vue CLI and Vuetify components\n- Build output goes to `api/public/` for serving by Go backend\n- Development server not typically used - Go server serves built assets\n\n### Backend Development  \n- Go application with CLI and API server\n- Uses Gorilla Mux for routing\n- Supports multiple databases: MySQL, PostgreSQL, SQLite (BoltDB was removed in 2.19)\n- Configuration via JSON file or environment variables\n\n## Troubleshooting\n\n### Build Issues\n- If `task` command not found: Install with `go install github.com/go-task/task/v3/cmd/task@latest`\n- If Go version errors: Ensure Go 1.21+ is installed\n- If npm install fails: Ensure Node.js 16+ is installed\n- If build takes too long: This is normal - frontend build can take 60+ seconds\n\n### Runtime Issues  \n- If server won't start: Check config.json exists and database is accessible\n- If web UI shows errors: Check that frontend build completed successfully in `api/public/`\n- If API returns errors: Check server logs for specific error messages\n\n### Database Issues\n\n- For development, use SQLite (option 4) during setup\n- The SQLite database file is created automatically at the configured path\n- If database errors occur, remove the SQLite file and run setup again\n- Configs with `\"dialect\": \"bolt\"` will fail at startup — switch to `sqlite` and re-run setup\n\n## Important Notes\n\n- **NEVER CANCEL** long-running builds or dependency installations\n- Set appropriate timeouts: deps (5+ min), build (3+ min), tests (2+ min)  \n- The application serves the frontend from the Go backend - no separate frontend server needed\n- Configuration is stored in `config.json` after running setup\n- Default admin credentials after setup: admin / changeme\n- Linting has known issues - focus on not introducing new ones\n- Always test changes by running the full application, not just unit tests"},"files":{".claude/CLAUDE.md":"# Claude Code Instructions\n\n## Writing Plans\n\nAll plans, tasks, researches for AI agents stored in folder AGENTS.\n\nEach plan has markdown-format and stored in folder AGENTS/plans/<version>.\n\nPlan can be split to tasks. Each task describes in details how to implement some part of some plan.\n\n## Code Style\n\n1. Do not use global variables. Global variables are forbidden.\n\n## High Availability Support\n\nAll solutions must work in High Availability mode.\n\n## Security Is the #1 Priority\n\nAll solutions must be secure by design. Do not consider any solution that introduces security risks.\n\n## How to do research\n\nUse MCP server `research` if your asks you to research something.\n\n## Writing Tests\n\nUse `github.com/stretchr/testify/assert` (and `require` when a failure should stop the test immediately).\n\n### Run tests\n\n```bash\ngo test ./path/to/package/ -run TestFunctionName -v -count=1\n```\n\n### Rules\n\n- Test file goes next to the source: `foo.go` → `foo_test.go`, same package.\n- Use `assert.Equal`, `assert.Empty`, `assert.Nil`, `assert.True`, `assert.Panics`, etc. Never use raw `if`/`t.Fatalf` for assertions.\n- Use `require` (instead of `assert`) when subsequent lines depend on the check passing (e.g., `require.NoError` before using a result).\n- Use table-driven tests with `t.Run` for multiple inputs to the same function.\n- Use `t.TempDir()` for temporary files — cleanup is automatic.\n- When testing code that uses package-level globals (e.g., `util.Config`), initialize them in a helper and reset state between tests.\n- When testing code that reads `os.Stdin`, swap it with `os.Pipe()` and restore in `defer`.\n\n### Template\n\n```go\npackage mypkg\n\nimport (\n    \"testing\"\n\n    \"github.com/stretchr/testify/assert\"\n    \"github.com/stretchr/testify/require\"\n)\n\nfunc TestMyFunction(t *testing.T) {\n    // setup\n    input := \"value\"\n\n    // act\n    result, err := MyFunction(input)\n\n    // assert\n    require.NoError(t, err)\n    assert.Equal(t, \"expected\", result)\n}\n\nfunc TestMyFunction_TableDriven(t *testing.T) {\n    tests := []struct {\n        name     string\n        input    string\n        expected string\n    }{\n        {\"short input\", \"abc\", \"ABC\"},\n        {\"empty input\", \"\", \"\"},\n    }\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            assert.Equal(t, tt.expected, MyFunction(tt.input))\n        })\n    }\n}\n\nfunc TestMyFunction_Panics(t *testing.T) {\n    assert.Panics(t, func() {\n        MyFunction(nil)\n    })\n}\n```\n\n### Common assertions\n\n| Assertion | Use when |\n|-----------|----------|\n| `assert.Equal(t, expected, actual)` | Comparing values |\n| `assert.NotEqual(t, a, b)` | Values must differ |\n| `assert.Nil(t, val)` / `assert.NotNil(t, val)` | Pointer/interface checks |\n| `assert.Empty(t, val)` | String, slice, or map is zero-length |\n| `assert.Contains(t, str, substr)` | Substring or element in collection |\n| `assert.True(t, cond)` / `assert.False(t, cond)` | Boolean conditions |\n| `assert.NoError(t, err)` / `assert.Error(t, err)` | Error checks |\n| `assert.ErrorContains(t, err, \"msg\")` | Error with specific message |\n| `assert.Panics(t, func(){...})` | Code must panic |\n| `require.NoError(t, err)` | Stop test immediately on error |\n\n### Initializing `util.Config` in tests\n\nMany packages depend on `util.Config` which is a `*ConfigType` pointer (nil by default). Initialize before use:\n\n```go\nfunc setupConfig() {\n    if util.Config == nil {\n        util.Config = &util.ConfigType{}\n    }\n    // Initialize nested pointers as needed:\n    if util.Config.Runner == nil {\n        util.Config.Runner = &util.RunnerConfig{}\n    }\n}\n```\n\n### HTTP handler tests\n\nUse `net/http/httptest`:\n\n```go\nfunc TestMyHandler(t *testing.T) {\n    req := httptest.NewRequest(http.MethodGet, \"/api/endpoint\", nil)\n    w := httptest.NewRecorder()\n\n    myHandler(w, req)\n\n    assert.Equal(t, http.StatusOK, w.Code)\n    assert.Contains(t, w.Body.String(), \"expected\")\n}\n```\n",".github/copilot-instructions.md":"# Semaphore UI Development Instructions\n\n**Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**\n\nSemaphore UI is a modern web interface for managing popular DevOps tools like Ansible, Terraform, PowerShell, and Bash scripts. It's built with Go (backend) and Vue.js (frontend), using Task runner for build automation.\n\n## Working Effectively\n\n### Bootstrap, build, and test the repository:\n- Install Go 1.21+ (currently requires go version 1.21 or higher)\n- Install Node.js 16+ \n- Install Task runner: `go install github.com/go-task/task/v3/cmd/task@latest`\n- Install dependencies: `task deps` -- takes 3 minutes first time (faster with cache). NEVER CANCEL. Set timeout to 5+ minutes.\n- Build the application: `task build` -- takes 1.5 minutes. NEVER CANCEL. Set timeout to 3+ minutes.\n- Run tests: `task test` -- takes 33 seconds. NEVER CANCEL. Set timeout to 2+ minutes.\n\n### Run the application:\n- ALWAYS run the bootstrapping steps first\n- Setup database and admin user: `./bin/semaphore setup` (interactive, use SQLite option 4 for development)\n- Start server: `./bin/semaphore server --config ./config.json`\n- Web UI: http://localhost:3000 (login: admin / changeme)\n- API: http://localhost:3000/api/ (test with: `curl http://localhost:3000/api/ping`)\n\n## Validation\n\n- **CRITICAL**: Always manually validate any new code by building and running the application.\n- ALWAYS run through at least one complete end-to-end scenario after making changes:\n  1. Build the application: `task build`\n  2. Start the server: `./bin/semaphore server --config ./config.json`\n  3. Test API endpoint: `curl http://localhost:3000/api/ping` (should return \"pong\")\n  4. Access web UI at http://localhost:3000 and verify it loads\n  5. For auth changes: Test login with admin/changeme\n- For significant changes, run full setup process to ensure setup still works\n- Always build and exercise your changes before considering the task complete\n\n### Complete Validation Scenario (for major changes):\n```bash\n# 1. Clean build\ntask build\n\n# 2. Setup database (if config.json doesn't exist)\n./bin/semaphore setup  # Choose option 4 (SQLite), use admin/changeme\n\n# 3. Start server\n./bin/semaphore server --config ./config.json\n\n# 4. Test in another terminal\ncurl http://localhost:3000/api/ping  # Should return \"pong\"\ncurl -I http://localhost:3000/       # Should return HTTP 200\n\n# 5. Test web interface manually in browser at http://localhost:3000\n# 6. Test login with admin/changeme if auth-related changes\n```\n\n### Linting and Code Quality\n\n- Frontend linting: `cd web && npm run lint` (has known warnings about console statements and asset sizes - ignore existing issues)\n- Backend linting: `golangci-lint run --timeout=3m` (has known type errors due to module import issues - ignore existing issues) \n- **DO NOT** try to fix existing linting issues unless specifically asked to\n- Always run linting on new code you add to ensure it follows project standards\n- Install golangci-lint if needed: `go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2`\n\n## Common Tasks\n\n### Repository Structure\n```\n.\n├── README.md           - Project documentation  \n├── CONTRIBUTING.md     - Development guidelines\n├── Taskfile.yml       - Task runner configuration\n├── go.mod             - Go module dependencies\n├── web/               - Vue.js frontend application\n│   ├── package.json   - Frontend dependencies\n│   ├── src/           - Vue.js source code\n│   └── public/        - Static assets\n├── cli/               - Go CLI application entry point  \n├── api/               - Go API server endpoints\n├── db/                - Database models and interfaces\n├── services/          - Business logic services\n├── util/              - Utility functions and configuration\n├── bin/               - Built binaries (after build)\n└── config.json       - Runtime configuration (after setup)\n```\n\n### Key Commands Reference\n```bash\n# Install task runner\ngo install github.com/go-task/task/v3/cmd/task@latest\n\n# Install all dependencies (backend + frontend + tools)\ntask deps\n\n# Build application (frontend + backend)\ntask build\n\n# Run tests\ntask test  \n\n# Run linting\ntask lint\n\n# Setup application (interactive)\n./bin/semaphore setup\n\n# Start server\n./bin/semaphore server --config ./config.json\n\n# View available task commands\ntask --list\n```\n\n### Database Options for Development\n\nDuring setup, choose option 4 (SQLite) for the simplest development setup:\n\n- No external database server required\n- Database file stored at the path configured in `config.json` (default under `/tmp/`)\n- Perfect for development and testing\n\n> **Note:** BoltDB (option 2) was removed in Semaphore 2.19. If you have an existing\n> `database.boltdb` file, migrate to SQLite, MySQL, or PostgreSQL before upgrading.\n\n### Frontend Development\n- Vue.js 2.x application in `web/` directory\n- Built with Vue CLI and Vuetify components\n- Build output goes to `api/public/` for serving by Go backend\n- Development server not typically used - Go server serves built assets\n\n### Backend Development  \n- Go application with CLI and API server\n- Uses Gorilla Mux for routing\n- Supports multiple databases: MySQL, PostgreSQL, SQLite (BoltDB was removed in 2.19)\n- Configuration via JSON file or environment variables\n\n## Troubleshooting\n\n### Build Issues\n- If `task` command not found: Install with `go install github.com/go-task/task/v3/cmd/task@latest`\n- If Go version errors: Ensure Go 1.21+ is installed\n- If npm install fails: Ensure Node.js 16+ is installed\n- If build takes too long: This is normal - frontend build can take 60+ seconds\n\n### Runtime Issues  \n- If server won't start: Check config.json exists and database is accessible\n- If web UI shows errors: Check that frontend build completed successfully in `api/public/`\n- If API returns errors: Check server logs for specific error messages\n\n### Database Issues\n\n- For development, use SQLite (option 4) during setup\n- The SQLite database file is created automatically at the configured path\n- If database errors occur, remove the SQLite file and run setup again\n- Configs with `\"dialect\": \"bolt\"` will fail at startup — switch to `sqlite` and re-run setup\n\n## Important Notes\n\n- **NEVER CANCEL** long-running builds or dependency installations\n- Set appropriate timeouts: deps (5+ min), build (3+ min), tests (2+ min)  \n- The application serves the frontend from the Go backend - no separate frontend server needed\n- Configuration is stored in `config.json` after running setup\n- Default admin credentials after setup: admin / changeme\n- Linting has known issues - focus on not introducing new ones\n- Always test changes by running the full application, not just unit tests"},"items":[{"name":"CLAUDE.md","path":".claude/CLAUDE.md","title":"CLAUDE.md","content":"# Claude Code Instructions\n\n## Writing Plans\n\nAll plans, tasks, researches for AI agents stored in folder AGENTS.\n\nEach plan has markdown-format and stored in folder AGENTS/plans/<version>.\n\nPlan can be split to tasks. Each task describes in details how to implement some part of some plan.\n\n## Code Style\n\n1. Do not use global variables. Global variables are forbidden.\n\n## High Availability Support\n\nAll solutions must work in High Availability mode.\n\n## Security Is the #1 Priority\n\nAll solutions must be secure by design. Do not consider any solution that introduces security risks.\n\n## How to do research\n\nUse MCP server `research` if your asks you to research something.\n\n## Writing Tests\n\nUse `github.com/stretchr/testify/assert` (and `require` when a failure should stop the test immediately).\n\n### Run tests\n\n```bash\ngo test ./path/to/package/ -run TestFunctionName -v -count=1\n```\n\n### Rules\n\n- Test file goes next to the source: `foo.go` → `foo_test.go`, same package.\n- Use `assert.Equal`, `assert.Empty`, `assert.Nil`, `assert.True`, `assert.Panics`, etc. Never use raw `if`/`t.Fatalf` for assertions.\n- Use `require` (instead of `assert`) when subsequent lines depend on the check passing (e.g., `require.NoError` before using a result).\n- Use table-driven tests with `t.Run` for multiple inputs to the same function.\n- Use `t.TempDir()` for temporary files — cleanup is automatic.\n- When testing code that uses package-level globals (e.g., `util.Config`), initialize them in a helper and reset state between tests.\n- When testing code that reads `os.Stdin`, swap it with `os.Pipe()` and restore in `defer`.\n\n### Template\n\n```go\npackage mypkg\n\nimport (\n    \"testing\"\n\n    \"github.com/stretchr/testify/assert\"\n    \"github.com/stretchr/testify/require\"\n)\n\nfunc TestMyFunction(t *testing.T) {\n    // setup\n    input := \"value\"\n\n    // act\n    result, err := MyFunction(input)\n\n    // assert\n    require.NoError(t, err)\n    assert.Equal(t, \"expected\", result)\n}\n\nfunc TestMyFunction_TableDriven(t *testing.T) {\n    tests := []struct {\n        name     string\n        input    string\n        expected string\n    }{\n        {\"short input\", \"abc\", \"ABC\"},\n        {\"empty input\", \"\", \"\"},\n    }\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            assert.Equal(t, tt.expected, MyFunction(tt.input))\n        })\n    }\n}\n\nfunc TestMyFunction_Panics(t *testing.T) {\n    assert.Panics(t, func() {\n        MyFunction(nil)\n    })\n}\n```\n\n### Common assertions\n\n| Assertion | Use when |\n|-----------|----------|\n| `assert.Equal(t, expected, actual)` | Comparing values |\n| `assert.NotEqual(t, a, b)` | Values must differ |\n| `assert.Nil(t, val)` / `assert.NotNil(t, val)` | Pointer/interface checks |\n| `assert.Empty(t, val)` | String, slice, or map is zero-length |\n| `assert.Contains(t, str, substr)` | Substring or element in collection |\n| `assert.True(t, cond)` / `assert.False(t, cond)` | Boolean conditions |\n| `assert.NoError(t, err)` / `assert.Error(t, err)` | Error checks |\n| `assert.ErrorContains(t, err, \"msg\")` | Error with specific message |\n| `assert.Panics(t, func(){...})` | Code must panic |\n| `require.NoError(t, err)` | Stop test immediately on error |\n\n### Initializing `util.Config` in tests\n\nMany packages depend on `util.Config` which is a `*ConfigType` pointer (nil by default). Initialize before use:\n\n```go\nfunc setupConfig() {\n    if util.Config == nil {\n        util.Config = &util.ConfigType{}\n    }\n    // Initialize nested pointers as needed:\n    if util.Config.Runner == nil {\n        util.Config.Runner = &util.RunnerConfig{}\n    }\n}\n```\n\n### HTTP handler tests\n\nUse `net/http/httptest`:\n\n```go\nfunc TestMyHandler(t *testing.T) {\n    req := httptest.NewRequest(http.MethodGet, \"/api/endpoint\", nil)\n    w := httptest.NewRecorder()\n\n    myHandler(w, req)\n\n    assert.Equal(t, http.StatusOK, w.Code)\n    assert.Contains(t, w.Body.String(), \"expected\")\n}\n```\n","category":".claude","tokens":981},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","title":"copilot-instructions.md","content":"# Semaphore UI Development Instructions\n\n**Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**\n\nSemaphore UI is a modern web interface for managing popular DevOps tools like Ansible, Terraform, PowerShell, and Bash scripts. It's built with Go (backend) and Vue.js (frontend), using Task runner for build automation.\n\n## Working Effectively\n\n### Bootstrap, build, and test the repository:\n- Install Go 1.21+ (currently requires go version 1.21 or higher)\n- Install Node.js 16+ \n- Install Task runner: `go install github.com/go-task/task/v3/cmd/task@latest`\n- Install dependencies: `task deps` -- takes 3 minutes first time (faster with cache). NEVER CANCEL. Set timeout to 5+ minutes.\n- Build the application: `task build` -- takes 1.5 minutes. NEVER CANCEL. Set timeout to 3+ minutes.\n- Run tests: `task test` -- takes 33 seconds. NEVER CANCEL. Set timeout to 2+ minutes.\n\n### Run the application:\n- ALWAYS run the bootstrapping steps first\n- Setup database and admin user: `./bin/semaphore setup` (interactive, use SQLite option 4 for development)\n- Start server: `./bin/semaphore server --config ./config.json`\n- Web UI: http://localhost:3000 (login: admin / changeme)\n- API: http://localhost:3000/api/ (test with: `curl http://localhost:3000/api/ping`)\n\n## Validation\n\n- **CRITICAL**: Always manually validate any new code by building and running the application.\n- ALWAYS run through at least one complete end-to-end scenario after making changes:\n  1. Build the application: `task build`\n  2. Start the server: `./bin/semaphore server --config ./config.json`\n  3. Test API endpoint: `curl http://localhost:3000/api/ping` (should return \"pong\")\n  4. Access web UI at http://localhost:3000 and verify it loads\n  5. For auth changes: Test login with admin/changeme\n- For significant changes, run full setup process to ensure setup still works\n- Always build and exercise your changes before considering the task complete\n\n### Complete Validation Scenario (for major changes):\n```bash\n# 1. Clean build\ntask build\n\n# 2. Setup database (if config.json doesn't exist)\n./bin/semaphore setup  # Choose option 4 (SQLite), use admin/changeme\n\n# 3. Start server\n./bin/semaphore server --config ./config.json\n\n# 4. Test in another terminal\ncurl http://localhost:3000/api/ping  # Should return \"pong\"\ncurl -I http://localhost:3000/       # Should return HTTP 200\n\n# 5. Test web interface manually in browser at http://localhost:3000\n# 6. Test login with admin/changeme if auth-related changes\n```\n\n### Linting and Code Quality\n\n- Frontend linting: `cd web && npm run lint` (has known warnings about console statements and asset sizes - ignore existing issues)\n- Backend linting: `golangci-lint run --timeout=3m` (has known type errors due to module import issues - ignore existing issues) \n- **DO NOT** try to fix existing linting issues unless specifically asked to\n- Always run linting on new code you add to ensure it follows project standards\n- Install golangci-lint if needed: `go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2`\n\n## Common Tasks\n\n### Repository Structure\n```\n.\n├── README.md           - Project documentation  \n├── CONTRIBUTING.md     - Development guidelines\n├── Taskfile.yml       - Task runner configuration\n├── go.mod             - Go module dependencies\n├── web/               - Vue.js frontend application\n│   ├── package.json   - Frontend dependencies\n│   ├── src/           - Vue.js source code\n│   └── public/        - Static assets\n├── cli/               - Go CLI application entry point  \n├── api/               - Go API server endpoints\n├── db/                - Database models and interfaces\n├── services/          - Business logic services\n├── util/              - Utility functions and configuration\n├── bin/               - Built binaries (after build)\n└── config.json       - Runtime configuration (after setup)\n```\n\n### Key Commands Reference\n```bash\n# Install task runner\ngo install github.com/go-task/task/v3/cmd/task@latest\n\n# Install all dependencies (backend + frontend + tools)\ntask deps\n\n# Build application (frontend + backend)\ntask build\n\n# Run tests\ntask test  \n\n# Run linting\ntask lint\n\n# Setup application (interactive)\n./bin/semaphore setup\n\n# Start server\n./bin/semaphore server --config ./config.json\n\n# View available task commands\ntask --list\n```\n\n### Database Options for Development\n\nDuring setup, choose option 4 (SQLite) for the simplest development setup:\n\n- No external database server required\n- Database file stored at the path configured in `config.json` (default under `/tmp/`)\n- Perfect for development and testing\n\n> **Note:** BoltDB (option 2) was removed in Semaphore 2.19. If you have an existing\n> `database.boltdb` file, migrate to SQLite, MySQL, or PostgreSQL before upgrading.\n\n### Frontend Development\n- Vue.js 2.x application in `web/` directory\n- Built with Vue CLI and Vuetify components\n- Build output goes to `api/public/` for serving by Go backend\n- Development server not typically used - Go server serves built assets\n\n### Backend Development  \n- Go application with CLI and API server\n- Uses Gorilla Mux for routing\n- Supports multiple databases: MySQL, PostgreSQL, SQLite (BoltDB was removed in 2.19)\n- Configuration via JSON file or environment variables\n\n## Troubleshooting\n\n### Build Issues\n- If `task` command not found: Install with `go install github.com/go-task/task/v3/cmd/task@latest`\n- If Go version errors: Ensure Go 1.21+ is installed\n- If npm install fails: Ensure Node.js 16+ is installed\n- If build takes too long: This is normal - frontend build can take 60+ seconds\n\n### Runtime Issues  \n- If server won't start: Check config.json exists and database is accessible\n- If web UI shows errors: Check that frontend build completed successfully in `api/public/`\n- If API returns errors: Check server logs for specific error messages\n\n### Database Issues\n\n- For development, use SQLite (option 4) during setup\n- The SQLite database file is created automatically at the configured path\n- If database errors occur, remove the SQLite file and run setup again\n- Configs with `\"dialect\": \"bolt\"` will fail at startup — switch to `sqlite` and re-run setup\n\n## Important Notes\n\n- **NEVER CANCEL** long-running builds or dependency installations\n- Set appropriate timeouts: deps (5+ min), build (3+ min), tests (2+ min)  \n- The application serves the frontend from the Go backend - no separate frontend server needed\n- Configuration is stored in `config.json` after running setup\n- Default admin credentials after setup: admin / changeme\n- Linting has known issues - focus on not introducing new ones\n- Always test changes by running the full application, not just unit tests","category":".github","tokens":1698}]}