Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
289 lines
5.8 KiB
Go
289 lines
5.8 KiB
Go
// cmd_repo.go implements repository setup with .core/ configuration.
|
|
//
|
|
// When running setup in an existing git repository, this generates
|
|
// build.yaml, release.yaml, and test.yaml configurations based on
|
|
// detected project type.
|
|
|
|
package setup
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/host-uk/core/pkg/i18n"
|
|
"github.com/host-uk/core/pkg/io"
|
|
)
|
|
|
|
// runRepoSetup sets up the current repository with .core/ configuration.
|
|
func runRepoSetup(repoPath string, dryRun bool) error {
|
|
fmt.Printf("%s %s: %s\n", dimStyle.Render(">>"), i18n.T("cmd.setup.repo.setting_up"), repoPath)
|
|
|
|
// Detect project type
|
|
projectType := detectProjectType(repoPath)
|
|
fmt.Printf("%s %s: %s\n", dimStyle.Render(">>"), i18n.T("cmd.setup.repo.detected_type"), projectType)
|
|
|
|
// Create .core directory
|
|
coreDir := filepath.Join(repoPath, ".core")
|
|
if !dryRun {
|
|
if err := io.Local.EnsureDir(coreDir); err != nil {
|
|
return fmt.Errorf("failed to create .core directory: %w", err)
|
|
}
|
|
}
|
|
|
|
// Generate configs based on project type
|
|
name := filepath.Base(repoPath)
|
|
configs := map[string]string{
|
|
"build.yaml": generateBuildConfig(repoPath, projectType),
|
|
"release.yaml": generateReleaseConfig(name, projectType),
|
|
"test.yaml": generateTestConfig(projectType),
|
|
}
|
|
|
|
if dryRun {
|
|
fmt.Printf("\n%s %s:\n", dimStyle.Render(">>"), i18n.T("cmd.setup.repo.would_create"))
|
|
for filename, content := range configs {
|
|
fmt.Printf("\n %s:\n", filepath.Join(coreDir, filename))
|
|
// Indent content for display
|
|
for _, line := range strings.Split(content, "\n") {
|
|
fmt.Printf(" %s\n", line)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
for filename, content := range configs {
|
|
configPath := filepath.Join(coreDir, filename)
|
|
if err := io.Local.Write(configPath, content); err != nil {
|
|
return fmt.Errorf("failed to write %s: %w", filename, err)
|
|
}
|
|
fmt.Printf("%s %s %s\n", successStyle.Render(">>"), i18n.T("cmd.setup.repo.created"), configPath)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// detectProjectType identifies the project type from files present.
|
|
func detectProjectType(path string) string {
|
|
// Check in priority order
|
|
if io.Local.IsFile(filepath.Join(path, "wails.json")) {
|
|
return "wails"
|
|
}
|
|
if io.Local.IsFile(filepath.Join(path, "go.mod")) {
|
|
return "go"
|
|
}
|
|
if io.Local.IsFile(filepath.Join(path, "composer.json")) {
|
|
return "php"
|
|
}
|
|
if io.Local.IsFile(filepath.Join(path, "package.json")) {
|
|
return "node"
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
// generateBuildConfig creates a build.yaml configuration based on project type.
|
|
func generateBuildConfig(path, projectType string) string {
|
|
name := filepath.Base(path)
|
|
|
|
switch projectType {
|
|
case "go", "wails":
|
|
return fmt.Sprintf(`version: 1
|
|
project:
|
|
name: %s
|
|
description: Go application
|
|
main: ./cmd/%s
|
|
binary: %s
|
|
build:
|
|
cgo: false
|
|
flags:
|
|
- -trimpath
|
|
ldflags:
|
|
- -s
|
|
- -w
|
|
targets:
|
|
- os: linux
|
|
arch: amd64
|
|
- os: linux
|
|
arch: arm64
|
|
- os: darwin
|
|
arch: amd64
|
|
- os: darwin
|
|
arch: arm64
|
|
- os: windows
|
|
arch: amd64
|
|
`, name, name, name)
|
|
|
|
case "php":
|
|
return fmt.Sprintf(`version: 1
|
|
project:
|
|
name: %s
|
|
description: PHP application
|
|
type: php
|
|
build:
|
|
dockerfile: Dockerfile
|
|
image: %s
|
|
`, name, name)
|
|
|
|
case "node":
|
|
return fmt.Sprintf(`version: 1
|
|
project:
|
|
name: %s
|
|
description: Node.js application
|
|
type: node
|
|
build:
|
|
script: npm run build
|
|
output: dist
|
|
`, name)
|
|
|
|
default:
|
|
return fmt.Sprintf(`version: 1
|
|
project:
|
|
name: %s
|
|
description: Application
|
|
`, name)
|
|
}
|
|
}
|
|
|
|
// generateReleaseConfig creates a release.yaml configuration.
|
|
func generateReleaseConfig(name, projectType string) string {
|
|
// Try to detect GitHub repo from git remote
|
|
repo := detectGitHubRepo()
|
|
if repo == "" {
|
|
repo = "owner/" + name
|
|
}
|
|
|
|
base := fmt.Sprintf(`version: 1
|
|
project:
|
|
name: %s
|
|
repository: %s
|
|
`, name, repo)
|
|
|
|
switch projectType {
|
|
case "go", "wails":
|
|
return base + `
|
|
changelog:
|
|
include:
|
|
- feat
|
|
- fix
|
|
- perf
|
|
- refactor
|
|
exclude:
|
|
- chore
|
|
- docs
|
|
- style
|
|
- test
|
|
|
|
publishers:
|
|
- type: github
|
|
draft: false
|
|
prerelease: false
|
|
`
|
|
case "php":
|
|
return base + `
|
|
changelog:
|
|
include:
|
|
- feat
|
|
- fix
|
|
- perf
|
|
|
|
publishers:
|
|
- type: github
|
|
draft: false
|
|
`
|
|
default:
|
|
return base + `
|
|
changelog:
|
|
include:
|
|
- feat
|
|
- fix
|
|
|
|
publishers:
|
|
- type: github
|
|
`
|
|
}
|
|
}
|
|
|
|
// generateTestConfig creates a test.yaml configuration.
|
|
func generateTestConfig(projectType string) string {
|
|
switch projectType {
|
|
case "go", "wails":
|
|
return `version: 1
|
|
|
|
commands:
|
|
- name: unit
|
|
run: go test ./...
|
|
- name: coverage
|
|
run: go test -coverprofile=coverage.out ./...
|
|
- name: race
|
|
run: go test -race ./...
|
|
|
|
env:
|
|
CGO_ENABLED: "0"
|
|
`
|
|
case "php":
|
|
return `version: 1
|
|
|
|
commands:
|
|
- name: unit
|
|
run: vendor/bin/pest --parallel
|
|
- name: types
|
|
run: vendor/bin/phpstan analyse
|
|
- name: lint
|
|
run: vendor/bin/pint --test
|
|
|
|
env:
|
|
APP_ENV: testing
|
|
DB_CONNECTION: sqlite
|
|
`
|
|
case "node":
|
|
return `version: 1
|
|
|
|
commands:
|
|
- name: unit
|
|
run: npm test
|
|
- name: lint
|
|
run: npm run lint
|
|
- name: typecheck
|
|
run: npm run typecheck
|
|
|
|
env:
|
|
NODE_ENV: test
|
|
`
|
|
default:
|
|
return `version: 1
|
|
|
|
commands:
|
|
- name: test
|
|
run: echo "No tests configured"
|
|
`
|
|
}
|
|
}
|
|
|
|
// detectGitHubRepo tries to extract owner/repo from git remote.
|
|
func detectGitHubRepo() string {
|
|
cmd := exec.Command("git", "remote", "get-url", "origin")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
url := strings.TrimSpace(string(output))
|
|
|
|
// Handle SSH format: git@github.com:owner/repo.git
|
|
if strings.HasPrefix(url, "git@github.com:") {
|
|
repo := strings.TrimPrefix(url, "git@github.com:")
|
|
repo = strings.TrimSuffix(repo, ".git")
|
|
return repo
|
|
}
|
|
|
|
// Handle HTTPS format: https://github.com/owner/repo.git
|
|
if strings.Contains(url, "github.com/") {
|
|
parts := strings.Split(url, "github.com/")
|
|
if len(parts) == 2 {
|
|
repo := strings.TrimSuffix(parts[1], ".git")
|
|
return repo
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|