Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/cli-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CLI CI

on:
push:
paths:
- 'cli/**'
- '.github/workflows/cli-ci.yml'
pull_request:
paths:
- 'cli/**'
- '.github/workflows/cli-ci.yml'

jobs:
build-and-test:
name: Build and test
runs-on: ubuntu-latest
defaults:
run:
working-directory: cli

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: cli/go.mod
cache-dependency-path: cli/go.sum

- name: go build
run: go build ./...

- name: go vet
run: go vet ./...

- name: go test
run: go test ./...
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
**/__pycache__/
**/.venv/

# Go CLI
cli/dist/
cli/osi
48 changes: 48 additions & 0 deletions cli/.goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: 2

project_name: ossie

before:
hooks:
- go mod tidy

builds:
- id: ossie
main: .
binary: ossie
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ignore:
- goos: windows
goarch: arm64
env:
- CGO_ENABLED=0
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}

archives:
- id: ossie
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

checksum:
name_template: "checksums.txt"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
1 change: 1 addition & 0 deletions cli/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
golang 1.26.2
19 changes: 19 additions & 0 deletions cli/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
BINARY_NAME := ossie
BUILD_DIR := dist

.PHONY: build test lint release-dry-run clean

build:
go build -o $(BUILD_DIR)/$(BINARY_NAME) .

test:
go test ./...

lint:
go vet ./...

release-dry-run:
goreleaser release --snapshot --clean --config .goreleaser.yaml

clean:
rm -rf $(BUILD_DIR)
39 changes: 39 additions & 0 deletions cli/cmd/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var convertCmd = &cobra.Command{
Use: "convert --from <platform> --input <path> | --to <platform> --input <path>",
Short: "Convert a semantic model between OSSIE and a platform format",
RunE: runConvert,
}

func init() {
convertCmd.Flags().String("from", "", "Source platform — converts platform → OSSIE")
convertCmd.Flags().String("to", "", "Target platform — converts OSSIE → platform")
convertCmd.Flags().StringP("input", "i", "", "Input file or directory path (required)")
convertCmd.Flags().StringP("output", "o", "", "Output directory path (default: ./ossie-output/<plugin>/<direction>)")
convertCmd.Flags().String("plugin", "", "Path to plugin directory (bypasses name-based discovery)")
convertCmd.Flags().Int("timeout", 60, "Plugin invocation timeout in seconds")
convertCmd.Flags().String("max-input-size", "100MB", "Maximum total input size (e.g. 500MB)")

_ = convertCmd.MarkFlagRequired("input")
convertCmd.MarkFlagsMutuallyExclusive("from", "to")
}

func runConvert(cmd *cobra.Command, args []string) error {
from, _ := cmd.Flags().GetString("from")
to, _ := cmd.Flags().GetString("to")

// MarkFlagsMutuallyExclusive handles the both-set case; handle neither here.
if from == "" && to == "" {
return fmt.Errorf("exactly one of --from or --to must be specified")
}

fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented")
return nil
}
22 changes: 22 additions & 0 deletions cli/cmd/plugin/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package plugin

import (
"fmt"

"github.com/spf13/cobra"
)

var installCmd = &cobra.Command{
Use: "install [name[@version] | url]",
Short: "Install a plugin from the registry or a URL",
RunE: runPluginInstall,
}

func init() {
installCmd.Flags().Bool("all", false, "Install the latest version of all registry plugins")
}

func runPluginInstall(cmd *cobra.Command, args []string) error {
fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented")
return nil
}
18 changes: 18 additions & 0 deletions cli/cmd/plugin/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package plugin

import (
"fmt"

"github.com/spf13/cobra"
)

var listCmd = &cobra.Command{
Use: "list",
Short: "List installed and available plugins",
RunE: runPluginList,
}

func runPluginList(cmd *cobra.Command, args []string) error {
fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented")
return nil
}
16 changes: 16 additions & 0 deletions cli/cmd/plugin/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package plugin

import "github.com/spf13/cobra"

// Cmd is the parent "ossie plugin" command. It is exported so cmd/root.go can
// register it. Invoking it bare prints help.
var Cmd = &cobra.Command{
Use: "plugin",
Short: "Manage OSSIE plugins",
}

func init() {
Cmd.AddCommand(listCmd)
Cmd.AddCommand(installCmd)
Cmd.AddCommand(removeCmd)
}
19 changes: 19 additions & 0 deletions cli/cmd/plugin/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package plugin

import (
"fmt"

"github.com/spf13/cobra"
)

var removeCmd = &cobra.Command{
Use: "remove <name>",
Short: "Remove an installed plugin",
Args: cobra.ExactArgs(1),
RunE: runPluginRemove,
}

func runPluginRemove(cmd *cobra.Command, args []string) error {
fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented")
return nil
}
38 changes: 38 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"github.com/open-semantic-interchange/ossie/cli/cmd/plugin"
"github.com/open-semantic-interchange/ossie/cli/internal/ossiedir"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "ossie",
Short: "Open Semantic Interchange CLI",
Long: `ossie is the command-line tool for the Open Semantic Interchange (OSSIE) project.`,
// NOTE: Cobra does NOT automatically chain PersistentPreRunE from parent to
// child. If any subcommand defines its own PersistentPreRunE or PreRunE, this
// function will not run for that subcommand. Future subcommands that define
// their own must call ossiedir.EnsurePluginDir() explicitly.
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return ossiedir.EnsurePluginDir()
},
}

// Execute runs the root command. Called by main.
func Execute() error {
return rootCmd.Execute()
}

// SetVersion sets the version string reported by `ossie --version`.
func SetVersion(v string) {
rootCmd.Version = v
}

func init() {
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Enable verbose output (shows plugin stderr)")

rootCmd.AddCommand(convertCmd)
rootCmd.AddCommand(validateCmd)
rootCmd.AddCommand(plugin.Cmd)
}
24 changes: 24 additions & 0 deletions cli/cmd/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var validateCmd = &cobra.Command{
Use: "validate [flags] <path> [<path>...]",
Short: "Validate one or more OSSIE YAML or JSON files",
Args: cobra.MinimumNArgs(1),
RunE: runValidate,
}

func init() {
validateCmd.Flags().Bool("strict", false, "Promote warnings to errors")
validateCmd.Flags().String("output", "text", "Output format: text or json")
}

func runValidate(cmd *cobra.Command, args []string) error {
fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented")
return nil
}
10 changes: 10 additions & 0 deletions cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/open-semantic-interchange/ossie/cli

go 1.22

require github.com/spf13/cobra v1.10.2

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect
)
10 changes: 10 additions & 0 deletions cli/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
40 changes: 40 additions & 0 deletions cli/internal/ossiedir/osidir.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ossiedir

import (
"fmt"
"os"
"path/filepath"
)

const (
defaultOSIDir = ".ossie"
pluginsSubdir = "plugins"
envVar = "OSSIE_PLUGIN_DIR"
)

// PluginDir returns the resolved plugin directory path.
// It respects $OSSIE_PLUGIN_DIR if set, otherwise defaults to ~/.ossie/plugins/.
func PluginDir() (string, error) {
if override := os.Getenv(envVar); override != "" {
return override, nil
}
// Use os.UserHomeDir rather than $HOME for Windows portability.
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("could not determine home directory: %w", err)
}
return filepath.Join(home, defaultOSIDir, pluginsSubdir), nil
}

// EnsurePluginDir ensures the plugin directory exists, creating it if needed.
// It is safe to call multiple times — os.MkdirAll is idempotent.
func EnsurePluginDir() error {
dir, err := PluginDir()
if err != nil {
return err
}
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("could not create plugin directory %s: %w", dir, err)
}
return nil
}
Loading
Loading