|
| 1 | +package cmd |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | + "fmt" |
| 6 | + "io" |
| 7 | + "log" |
| 8 | + "os" |
| 9 | + "path/filepath" |
| 10 | + |
| 11 | + "github.com/hasura/ndc-hub/registry-automation/pkg/ndchub" |
| 12 | + "github.com/spf13/cobra" |
| 13 | +) |
| 14 | + |
| 15 | +var downloadArtifactsCmd = &cobra.Command{ |
| 16 | + Use: "download-artifacts", |
| 17 | + Short: "Downloads the artifacts from the connector registry", |
| 18 | + Run: runDownloadArtifactsCmd, |
| 19 | +} |
| 20 | + |
| 21 | +var downloadArtifactsCmdArgs ConnectorRegistryArgs |
| 22 | + |
| 23 | +func init() { |
| 24 | + RootCmd.AddCommand(downloadArtifactsCmd) |
| 25 | + var changedFilesPathEnv = os.Getenv("CHANGED_FILES_PATH") // this file contains the list of changed files |
| 26 | + downloadArtifactsCmd.PersistentFlags().StringVar(&downloadArtifactsCmdArgs.ChangedFilesPath, "changed-files-path", changedFilesPathEnv, "path to a line-separated list of changed files in the PR") |
| 27 | + if changedFilesPathEnv == "" { |
| 28 | + downloadArtifactsCmd.MarkPersistentFlagRequired("changed-files-path") |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +type ArtifactDownloadOptions struct { |
| 33 | + ChangedFilesPath string |
| 34 | + SingleFilePath string |
| 35 | +} |
| 36 | + |
| 37 | +type ArtifactOption func(*ArtifactDownloadOptions) |
| 38 | + |
| 39 | +func WithChangedFilesPath(path string) ArtifactOption { |
| 40 | + return func(o *ArtifactDownloadOptions) { |
| 41 | + o.ChangedFilesPath = path |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +func WithSingleFile(path string) ArtifactOption { |
| 46 | + return func(o *ArtifactDownloadOptions) { |
| 47 | + o.SingleFilePath = path |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +func DownloadArtifacts(opts ...ArtifactOption) ([]*ndchub.ConnectorArtifacts, error) { |
| 52 | + options := &ArtifactDownloadOptions{} |
| 53 | + for _, opt := range opts { |
| 54 | + opt(options) |
| 55 | + } |
| 56 | + |
| 57 | + if options.ChangedFilesPath == "" && options.SingleFilePath == "" { |
| 58 | + return nil, fmt.Errorf("at least one of ChangedFilesPath or SingleFilePath must be provided") |
| 59 | + } |
| 60 | + |
| 61 | + if options.ChangedFilesPath != "" { |
| 62 | + connectorPackagingFiles, err := getConnectorPackagingFilesFromChangedFiles(options.ChangedFilesPath) |
| 63 | + if err != nil { |
| 64 | + return nil, fmt.Errorf("failed to get connector packaging files from changed files: %w", err) |
| 65 | + } |
| 66 | + return downloadArtifacts(connectorPackagingFiles) |
| 67 | + } else { |
| 68 | + // If a single file path is provided, we can directly download the artifacts for that file |
| 69 | + artifacts, err := downloadArtifactsUtil(options.SingleFilePath) |
| 70 | + artifactsArr := []*ndchub.ConnectorArtifacts{artifacts} |
| 71 | + return artifactsArr, err |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +func getChangedFiles(filepath string) (*ChangedFiles, error) { |
| 76 | + changedFilesContent, err := os.Open(filepath) |
| 77 | + if err != nil { |
| 78 | + // log.Fatalf("Failed to open the file: %v, err: %v", ciCmdArgs.ChangedFilesPath, err) |
| 79 | + return nil, fmt.Errorf("failed to open the file: %v, err: %w", downloadArtifactsCmdArgs.ChangedFilesPath, err) |
| 80 | + } |
| 81 | + defer changedFilesContent.Close() |
| 82 | + |
| 83 | + // Read the changed file's contents. This file contains all the changed files in the PR |
| 84 | + changedFilesByteValue, err := io.ReadAll(changedFilesContent) |
| 85 | + if err != nil { |
| 86 | + // log.Fatalf("Failed to read the changed files JSON file: %v", err) |
| 87 | + return nil, fmt.Errorf("failed to read the changed files JSON file: %w", err) |
| 88 | + } |
| 89 | + |
| 90 | + var changedFiles *ChangedFiles = &ChangedFiles{} |
| 91 | + err = json.Unmarshal(changedFilesByteValue, changedFiles) |
| 92 | + if err != nil { |
| 93 | + // log.Fatalf("Failed to unmarshal the changed files content: %v", err) |
| 94 | + return nil, fmt.Errorf("failed to unmarshal the changed files content: %w", err) |
| 95 | + } |
| 96 | + |
| 97 | + return changedFiles, nil |
| 98 | +} |
| 99 | + |
| 100 | +// for now, since we're only adding support for newly added connectors, we will only check for the added files |
| 101 | +// in the added files, we only need files that end with connector-packaging.json |
| 102 | +func filterConnectorPackagingFiles(changedFiles *ChangedFiles) []string { |
| 103 | + // Filter the changed files to only include the ones that are in the connector registry |
| 104 | + var filteredChangedFiles []string = make([]string, 0) |
| 105 | + for _, file := range changedFiles.Added { |
| 106 | + if isConnectorPackagingFile(file) { |
| 107 | + filteredChangedFiles = append(filteredChangedFiles, file) |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + log.Printf("Filtered connector packaging files: %v", filteredChangedFiles) |
| 112 | + return filteredChangedFiles |
| 113 | +} |
| 114 | + |
| 115 | +func isConnectorPackagingFile(path string) bool { |
| 116 | + return filepath.Base(path) == "connector-packaging.json" |
| 117 | +} |
| 118 | + |
| 119 | +func getConnectorPackaging(filePath string) (*ndchub.ConnectorPackaging, error) { |
| 120 | + if !filepath.IsAbs(filePath) { |
| 121 | + filePath = filepath.Join("../", filePath) // the filepaths returned by the changed files action are relative to the root of the repository. Therefore, we need to prepend "../" to the path to get the correct path |
| 122 | + } |
| 123 | + ndcHubConnectorPackaging, err := ndchub.GetConnectorPackaging(filePath) |
| 124 | + if err != nil { |
| 125 | + return nil, fmt.Errorf("failed to get the connector packaging for file %s: %w", filePath, err) |
| 126 | + } |
| 127 | + return ndcHubConnectorPackaging, nil |
| 128 | +} |
| 129 | + |
| 130 | +func getConnectorPackagingFilesFromChangedFiles(changedFilesPath string) ([]string, error) { |
| 131 | + // Get the changed files from the PR |
| 132 | + changedFiles, err := getChangedFiles(changedFilesPath) |
| 133 | + if err != nil { |
| 134 | + return nil, err |
| 135 | + } |
| 136 | + |
| 137 | + // Get the connector packaging files (connector-packaging.json) from the changed files |
| 138 | + connectorPackagingFiles := filterConnectorPackagingFiles(changedFiles) |
| 139 | + return connectorPackagingFiles, nil |
| 140 | +} |
| 141 | + |
| 142 | +func downloadArtifacts(connectorPackagingFiles []string) ([]*ndchub.ConnectorArtifacts, error) { |
| 143 | + artifactList := make([]*ndchub.ConnectorArtifacts, 0) |
| 144 | + for _, file := range connectorPackagingFiles { |
| 145 | + log.Printf("\n\n") // for more readable logs |
| 146 | + artifacts, err := downloadArtifactsUtil(file) |
| 147 | + if err != nil { |
| 148 | + return nil, fmt.Errorf("failed to download artifacts for file %s: %w", file, err) |
| 149 | + } |
| 150 | + artifactList = append(artifactList, artifacts) |
| 151 | + } |
| 152 | + |
| 153 | + return artifactList, nil |
| 154 | +} |
| 155 | + |
| 156 | +func downloadArtifactsUtil(connectorPackagingFilePath string) (*ndchub.ConnectorArtifacts, error) { |
| 157 | + connectorPackaging, err := getConnectorPackaging(connectorPackagingFilePath) |
| 158 | + if err != nil { |
| 159 | + return nil, fmt.Errorf("failed to get the connector packaging: %w", err) |
| 160 | + } |
| 161 | + |
| 162 | + connectorMetadata, _, extractedTgzPath, err := ndchub.GetPackagingSpec(connectorPackaging.URI, |
| 163 | + connectorPackaging.Namespace, |
| 164 | + connectorPackaging.Name, |
| 165 | + connectorPackaging.Version, |
| 166 | + ) |
| 167 | + |
| 168 | + if err != nil { |
| 169 | + return nil, fmt.Errorf("failed to get connector metadata for %s/%s:%s: %w", |
| 170 | + connectorPackaging.Namespace, connectorPackaging.Name, connectorPackaging.Version, err) |
| 171 | + } |
| 172 | + |
| 173 | + artifacts, err := connectorMetadata.GetArtifacts(extractedTgzPath) |
| 174 | + if err != nil { |
| 175 | + return nil, fmt.Errorf("failed to get the artifacts for %s/%s:%s: %w", |
| 176 | + connectorPackaging.Namespace, connectorPackaging.Name, connectorPackaging.Version, err) |
| 177 | + } |
| 178 | + |
| 179 | + return artifacts, nil |
| 180 | +} |
| 181 | + |
| 182 | +func runDownloadArtifactsCmd(cmd *cobra.Command, args []string) { |
| 183 | + artifacts, err := DownloadArtifacts(WithChangedFilesPath(downloadArtifactsCmdArgs.ChangedFilesPath)) |
| 184 | + if err != nil { |
| 185 | + fmt.Printf("Failed to download artifacts: %v\n", err) |
| 186 | + os.Exit(1) |
| 187 | + } |
| 188 | + |
| 189 | + // Print artifactList as JSON |
| 190 | + artifactListJSON, err := json.MarshalIndent(artifacts, "", " ") |
| 191 | + if err != nil { |
| 192 | + fmt.Printf("Failed to marshal artifact list to JSON: %v\n", err) |
| 193 | + return |
| 194 | + } |
| 195 | + fmt.Println(string(artifactListJSON)) |
| 196 | +} |
0 commit comments