147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/user/aevs/internal/archiver"
|
|
"github.com/user/aevs/internal/config"
|
|
"github.com/user/aevs/internal/storage"
|
|
"github.com/user/aevs/internal/types"
|
|
)
|
|
|
|
var (
|
|
pushConfig string
|
|
pushDryRun bool
|
|
)
|
|
|
|
var pushCmd = &cobra.Command{
|
|
Use: "push",
|
|
Short: "Push env files to storage",
|
|
Long: `Upload local .env files to cloud storage.`,
|
|
RunE: runPush,
|
|
}
|
|
|
|
func init() {
|
|
pushCmd.Flags().StringVarP(&pushConfig, "config", "c", config.DefaultProjectConfigFile, "path to project config")
|
|
pushCmd.Flags().BoolVar(&pushDryRun, "dry-run", false, "show what would be uploaded without uploading")
|
|
}
|
|
|
|
func runPush(cmd *cobra.Command, args []string) error {
|
|
// Load global config
|
|
globalCfg, err := config.LoadGlobalConfig()
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("no storage configured; run 'aevs config' first")
|
|
}
|
|
return fmt.Errorf("failed to load global config: %w", err)
|
|
}
|
|
|
|
// Load project config
|
|
projectCfg, err := config.LoadProjectConfig(pushConfig)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("no aevs.yaml found; run 'aevs init' first")
|
|
}
|
|
return fmt.Errorf("failed to load project config: %w", err)
|
|
}
|
|
|
|
currentDir, err := os.Getwd()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get current directory: %w", err)
|
|
}
|
|
|
|
// Check that all files exist
|
|
var fileSizes []int64
|
|
for _, file := range projectCfg.Files {
|
|
fullPath := filepath.Join(currentDir, file)
|
|
info, err := os.Stat(fullPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("file not found: %s", file)
|
|
}
|
|
return fmt.Errorf("failed to stat file %s: %w", file, err)
|
|
}
|
|
fileSizes = append(fileSizes, info.Size())
|
|
}
|
|
|
|
// Create archive
|
|
archive, archiveSize, err := archiver.Create(projectCfg.Files, currentDir)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create archive: %w", err)
|
|
}
|
|
|
|
if pushDryRun {
|
|
fmt.Println()
|
|
fmt.Println("Dry run - no changes will be made")
|
|
fmt.Println()
|
|
fmt.Println("Would upload:")
|
|
for i, file := range projectCfg.Files {
|
|
fmt.Printf(" %s (%d bytes)\n", file, fileSizes[i])
|
|
}
|
|
fmt.Println()
|
|
fmt.Printf("Target: s3://%s/%s/%s\n", globalCfg.Storage.Bucket, projectCfg.Project, config.ArchiveFileName)
|
|
return nil
|
|
}
|
|
|
|
// Create storage client
|
|
s3Storage, err := storage.NewS3Storage(&globalCfg.Storage)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create storage client: %w", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
fmt.Println()
|
|
fmt.Printf("Pushing %q to storage...\n", projectCfg.Project)
|
|
fmt.Println()
|
|
|
|
// Upload archive
|
|
archiveKey := fmt.Sprintf("%s/%s", projectCfg.Project, config.ArchiveFileName)
|
|
if err := s3Storage.Upload(ctx, archiveKey, archive, archiveSize); err != nil {
|
|
return fmt.Errorf("failed to upload archive: %w", err)
|
|
}
|
|
|
|
// Print file list
|
|
for i, file := range projectCfg.Files {
|
|
fmt.Printf(" ✓ %s (%d bytes)\n", file, fileSizes[i])
|
|
}
|
|
fmt.Println()
|
|
|
|
// Get hostname
|
|
hostname, _ := os.Hostname()
|
|
if hostname == "" {
|
|
hostname = "unknown"
|
|
}
|
|
|
|
// Create and upload metadata
|
|
metadata := types.Metadata{
|
|
UpdatedAt: time.Now(),
|
|
Files: projectCfg.Files,
|
|
Machine: hostname,
|
|
SizeBytes: archiveSize,
|
|
}
|
|
|
|
metadataJSON, err := json.Marshal(metadata)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal metadata: %w", err)
|
|
}
|
|
|
|
metadataKey := fmt.Sprintf("%s/%s", projectCfg.Project, config.MetadataFileName)
|
|
if err := s3Storage.Upload(ctx, metadataKey, bytes.NewReader(metadataJSON), int64(len(metadataJSON))); err != nil {
|
|
return fmt.Errorf("failed to upload metadata: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Uploaded to s3://%s/%s\n", globalCfg.Storage.Bucket, archiveKey)
|
|
fmt.Printf("Total: %d files, %d bytes\n", len(projectCfg.Files), archiveSize)
|
|
|
|
return nil
|
|
}
|