Skip to content
Merged
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
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ clean:
rm -rf ${API_DIR}/zz_generated.go
rm -f ./mass

.PHONY: docs
docs: build
./mass docs

.PHONY: generate
generate:
curl -s ${SCHEMA_URL} -o ${API_DIR}/schema.graphql
Expand All @@ -44,6 +48,17 @@ bin:
lint:
golangci-lint run

.PHONY: build
build:
@if [ "$$(uname -s)" = "Darwin" ]; then \
$(MAKE) build.macos; \
elif [ "$$(uname -s)" = "Linux" ]; then \
$(MAKE) build.linux; \
else \
echo "Error: Unsupported operating system. Please use 'make build.macos' or 'make build.linux' directly."; \
exit 1; \
fi

.PHONY: build.macos
build.macos: bin
GOOS=darwin GOARCH=arm64 go build -o bin/mass-darwin-arm64 -ldflags=${LD_FLAGS}
Expand Down
170 changes: 170 additions & 0 deletions cmd/artifact.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
package cmd

import (
"bytes"
"context"
"embed"
"encoding/json"
"fmt"
"text/template"

"github.com/charmbracelet/glamour"
"github.com/massdriver-cloud/mass/docs/helpdocs"
"github.com/massdriver-cloud/mass/pkg/api"
"github.com/massdriver-cloud/mass/pkg/artifact"
artifactcmd "github.com/massdriver-cloud/mass/pkg/commands/artifact"
"github.com/massdriver-cloud/massdriver-sdk-go/massdriver/client"
"github.com/spf13/cobra"
)

//go:embed templates/artifact.get.md.tmpl
var artifactTemplates embed.FS

func NewCmdArtifact() *cobra.Command {
artifactCmd := &cobra.Command{
Use: "artifact",
Expand All @@ -28,11 +37,45 @@
artifactImportCmd.Flags().StringP("name", "n", "", "Artifact name")
artifactImportCmd.Flags().StringP("type", "t", "", "Artifact type")
artifactImportCmd.Flags().StringP("file", "f", "", "Artifact file")
artifactImportCmd.MarkFlagRequired("name")

Check failure on line 40 in cmd/artifact.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `artifactImportCmd.MarkFlagRequired` is not checked (errcheck)
artifactImportCmd.MarkFlagRequired("type")

Check failure on line 41 in cmd/artifact.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `artifactImportCmd.MarkFlagRequired` is not checked (errcheck)
artifactImportCmd.MarkFlagRequired("file")

Check failure on line 42 in cmd/artifact.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `artifactImportCmd.MarkFlagRequired` is not checked (errcheck)

// Get
artifactGetCmd := &cobra.Command{
Use: "get [artifact-id]",
Short: "Get an artifact from Massdriver",
Long: helpdocs.MustRender("artifact/get"),
Args: cobra.ExactArgs(1),
RunE: runArtifactGet,
Example: ` # Get artifact using UUID (imported artifacts)
mass artifact get 12345678-1234-1234-1234-123456789012

# Get artifact using friendly slug (provisioned artifacts)
mass artifact get api-prod-database-connection
mass artifact get api-prod-grpcapi-host -o json`,
}
artifactGetCmd.Flags().StringP("output", "o", "text", "Output format (text or json)")

// Download
artifactDownloadCmd := &cobra.Command{
Use: "download [artifact-id]",
Short: "Download an artifact in the specified format",
Long: helpdocs.MustRender("artifact/download"),
Args: cobra.ExactArgs(1),
RunE: runArtifactDownload,
Example: ` # Download artifact using UUID (imported artifacts)
mass artifact download 12345678-1234-1234-1234-123456789012

# Download artifact using friendly slug (provisioned artifacts)
mass artifact download api-prod-database-connection
mass artifact download network-useast1-vpc-network -f yaml`,
}
artifactDownloadCmd.Flags().StringP("format", "f", "json", "Download format (json, yaml, etc.)")

artifactCmd.AddCommand(artifactImportCmd)
artifactCmd.AddCommand(artifactGetCmd)
artifactCmd.AddCommand(artifactDownloadCmd)

return artifactCmd
}
Expand Down Expand Up @@ -68,3 +111,130 @@
_, importErr := artifactcmd.RunImport(ctx, mdClient, promptData.Name, promptData.Type, promptData.File)
return importErr
}

func runArtifactGet(cmd *cobra.Command, args []string) error {
ctx := context.Background()

artifactID := args[0]
outputFormat, err := cmd.Flags().GetString("output")
if err != nil {
return err
}
cmd.SilenceUsage = true

mdClient, mdClientErr := client.New()
if mdClientErr != nil {
return fmt.Errorf("error initializing massdriver client: %w", mdClientErr)
}

artifact, getErr := api.GetArtifact(ctx, mdClient, artifactID)
if getErr != nil {
return fmt.Errorf("error getting artifact: %w", getErr)
}

switch outputFormat {
case "json":
jsonBytes, err := json.MarshalIndent(artifact, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal artifact to JSON: %w", err)
}
fmt.Println(string(jsonBytes))
case "text":
err = renderArtifact(artifact)
if err != nil {
return err
}
default:
return fmt.Errorf("unsupported output format: %s", outputFormat)
}

return nil
}

func runArtifactDownload(cmd *cobra.Command, args []string) error {
ctx := context.Background()

artifactID := args[0]
format, err := cmd.Flags().GetString("format")
if err != nil {
return err
}
cmd.SilenceUsage = true

mdClient, mdClientErr := client.New()
if mdClientErr != nil {
return fmt.Errorf("error initializing massdriver client: %w", mdClientErr)
}

rendered, downloadErr := api.DownloadArtifact(ctx, mdClient, artifactID, format)
if downloadErr != nil {
return fmt.Errorf("error downloading artifact: %w", downloadErr)
}

fmt.Print(rendered)
return nil
}

func renderArtifact(artifact *api.Artifact) error {
specsJSON := "{}"
if artifact.Specs != nil {
specsBytes, err := json.MarshalIndent(artifact.Specs, "", " ")
if err == nil {
specsJSON = string(specsBytes)
}
}

tmplBytes, err := artifactTemplates.ReadFile("templates/artifact.get.md.tmpl")
if err != nil {
return fmt.Errorf("failed to read template: %w", err)
}

tmpl, err := template.New("artifact").Parse(string(tmplBytes))
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}

data := struct {
ID string
Name string
Type string
Field string
Origin string
SpecsJSON string
Formats []string
CreatedAt string
UpdatedAt string
ArtifactDefinition *api.ArtifactDefinitionWithSchema
Package *api.ArtifactPackage
}{
ID: artifact.ID,
Name: artifact.Name,
Type: artifact.Type,
Field: artifact.Field,
Origin: artifact.Origin,
SpecsJSON: specsJSON,
Formats: artifact.Formats,
CreatedAt: artifact.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: artifact.UpdatedAt.Format("2006-01-02 15:04:05"),
ArtifactDefinition: artifact.ArtifactDefinition,
Package: artifact.Package,
}

var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}

r, err := glamour.NewTermRenderer(glamour.WithAutoStyle())
if err != nil {
return err
}

out, err := r.Render(buf.String())
if err != nil {
return err
}

fmt.Print(out)
return nil
}
56 changes: 56 additions & 0 deletions cmd/definition.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package cmd

import (
"bufio"
"bytes"
"context"
"embed"
"encoding/json"
"fmt"
"os"
"strings"
"text/template"

"github.com/charmbracelet/glamour"
Expand Down Expand Up @@ -56,9 +58,19 @@ func NewCmdDefinition() *cobra.Command {
definitionPublishCmd.Flags().StringP("file", "f", "", "File containing artifact definition schema (use - for stdin)")
_ = definitionPublishCmd.MarkFlagRequired("file")

definitionDeleteCmd := &cobra.Command{
Use: "delete [definition]",
Short: "Delete an artifact definition from Massdriver",
Long: helpdocs.MustRender("definition/delete"),
Args: cobra.ExactArgs(1),
RunE: runDefinitionDelete,
}
definitionDeleteCmd.Flags().BoolP("force", "f", false, "Skip confirmation prompt")

definitionCmd.AddCommand(definitionGetCmd)
definitionCmd.AddCommand(definitionPublishCmd)
definitionCmd.AddCommand(definitionListCmd)
definitionCmd.AddCommand(definitionDeleteCmd)

return definitionCmd
}
Expand Down Expand Up @@ -203,3 +215,47 @@ func renderDefinition(ad *api.ArtifactDefinitionWithSchema) error {
fmt.Print(out)
return nil
}

func runDefinitionDelete(cmd *cobra.Command, args []string) error {
ctx := context.Background()

definitionName := args[0]
force, err := cmd.Flags().GetBool("force")
if err != nil {
return err
}
cmd.SilenceUsage = true

mdClient, mdClientErr := client.New()
if mdClientErr != nil {
return fmt.Errorf("error initializing massdriver client: %w", mdClientErr)
}

// Get definition details for confirmation
ad, getErr := definition.Get(ctx, mdClient, definitionName)
if getErr != nil {
return fmt.Errorf("error getting artifact definition: %w", getErr)
}

// Prompt for confirmation - requires typing the definition name unless --force is used
if !force {
fmt.Printf("WARNING: This will permanently delete artifact definition `%s`.\n", ad.Name)
fmt.Printf("Type `%s` to confirm deletion: ", ad.Name)
reader := bufio.NewReader(os.Stdin)
answer, _ := reader.ReadString('\n')
answer = strings.TrimSpace(answer)

if answer != ad.Name {
fmt.Println("Deletion cancelled.")
return nil
}
}

deletedDef, deleteErr := api.DeleteArtifactDefinition(ctx, mdClient, definitionName)
if deleteErr != nil {
return fmt.Errorf("error deleting artifact definition: %w", deleteErr)
}

fmt.Printf("Artifact definition %s deleted successfully!\n", prettylogs.Underline(deletedDef.Name))
return nil
}
50 changes: 50 additions & 0 deletions cmd/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cmd

import (
"context"
"fmt"
"os"

"github.com/massdriver-cloud/mass/docs/helpdocs"
"github.com/massdriver-cloud/mass/pkg/api"
"github.com/massdriver-cloud/massdriver-sdk-go/massdriver/client"
"github.com/spf13/cobra"
)

func NewCmdLogs() *cobra.Command {
logsCmd := &cobra.Command{
Use: "logs [deployment-id]",
Short: "Get deployment logs",
Long: helpdocs.MustRender("logs"),
Args: cobra.ExactArgs(1),
RunE: runLogs,
Example: ` # Get logs for a deployment
mass logs 12345678-1234-1234-1234-123456789012`,
}

return logsCmd
}

func runLogs(cmd *cobra.Command, args []string) error {
ctx := context.Background()

deploymentID := args[0]
cmd.SilenceUsage = true

mdClient, mdClientErr := client.New()
if mdClientErr != nil {
return fmt.Errorf("error initializing massdriver client: %w", mdClientErr)
}

logs, err := api.GetDeploymentLogs(ctx, mdClient, deploymentID)
if err != nil {
return fmt.Errorf("error getting deployment logs: %w", err)
}

// Output logs to stdout
for _, log := range logs {
fmt.Fprint(os.Stdout, log.Content)
}

return nil
}
Loading
Loading