Skip to content

Commit 83fbf58

Browse files
shivanand-harnessHarness
authored andcommitted
fix: [AH-2473]: fix issue with migrating npm packages from jfrog to har (#134)
* 54ae82 fix: [AH-2473]: fix issue with migrating npm packages from jfrog to har
1 parent a855359 commit 83fbf58

File tree

3 files changed

+187
-222
lines changed

3 files changed

+187
-222
lines changed

cmd/artifact/command/push_npm.go

Lines changed: 17 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package command
22

33
import (
4-
"archive/tar"
5-
"compress/gzip"
64
"context"
7-
"encoding/base64"
85
"encoding/json"
96
"errors"
107
"fmt"
@@ -13,10 +10,10 @@ import (
1310
"os"
1411
"path/filepath"
1512

13+
"github.com/harness/harness-cli/cmd/artifact/command/utils"
1614
"github.com/harness/harness-cli/cmd/cmdutils"
1715
"github.com/harness/harness-cli/config"
1816
pkgclient "github.com/harness/harness-cli/internal/api/ar_pkg"
19-
"github.com/harness/harness-cli/module/ar/migrate/types/npm"
2017
"github.com/harness/harness-cli/util/common/auth"
2118
p "github.com/harness/harness-cli/util/common/progress"
2219

@@ -72,15 +69,29 @@ func NewPushNpmCmd(f *cmdutils.Factory) *cobra.Command {
7269

7370
// Extract package.json from tarball
7471
progress.Step("Extracting package.json from tarball")
75-
pkgJSONBytes, err := extractPackageJSONFromTarball(packageFilePath)
72+
file, err := os.Open(packageFilePath)
73+
if err != nil {
74+
progress.Error("Failed to open tarball")
75+
return fmt.Errorf("failed to open tarball: %w", err)
76+
}
77+
defer file.Close()
78+
79+
pkgJSONBytes, err := utils.ExtractPackageJSONFromTarball(file)
7680
if err != nil {
7781
progress.Error("Failed to extract package.json from tarball")
7882
return fmt.Errorf("failed to extract package.json from tarball: %w", err)
7983
}
8084

8185
// Build NPM upload payload
86+
file, err = os.Open(packageFilePath)
87+
if err != nil {
88+
progress.Error("Failed to open tarball")
89+
return fmt.Errorf("failed to open tarball: %w", err)
90+
}
91+
defer file.Close()
92+
8293
progress.Step("Building NPM upload payload")
83-
upload, pkgName, version, err := buildNpmUploadFromPackageJSON(pkgJSONBytes, packageFilePath)
94+
upload, pkgName, version, err := utils.BuildNpmUploadFromPackageJSON(pkgJSONBytes, file)
8495
if err != nil {
8596
progress.Error("Failed to build NPM upload body")
8697
return fmt.Errorf("failed to build NPM upload body: %w", err)
@@ -156,144 +167,3 @@ func NewPushNpmCmd(f *cmdutils.Factory) *cobra.Command {
156167

157168
return cmd
158169
}
159-
160-
func extractPackageJSONFromTarball(path string) ([]byte, error) {
161-
file, err := os.Open(path)
162-
if err != nil {
163-
return nil, fmt.Errorf("failed to open tarball: %w", err)
164-
}
165-
defer file.Close()
166-
167-
gzReader, err := gzip.NewReader(file)
168-
if err != nil {
169-
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
170-
}
171-
defer gzReader.Close()
172-
173-
tarReader := tar.NewReader(gzReader)
174-
175-
for {
176-
header, err := tarReader.Next()
177-
if err == io.EOF {
178-
break
179-
}
180-
if err != nil {
181-
return nil, fmt.Errorf("failed to read tar header: %w", err)
182-
}
183-
184-
if header.FileInfo().IsDir() {
185-
continue
186-
}
187-
188-
base := filepath.Base(header.Name)
189-
if base == "package.json" {
190-
data, err := io.ReadAll(tarReader)
191-
if err != nil {
192-
return nil, fmt.Errorf("failed to read package.json from tarball: %w", err)
193-
}
194-
return data, nil
195-
}
196-
}
197-
198-
return nil, fmt.Errorf("package.json not found in tarball")
199-
}
200-
201-
// minimalPackageJSON represents the subset of fields from package.json we care about.
202-
type minimalPackageJSON struct {
203-
Name string `json:"name"`
204-
Version string `json:"version"`
205-
Description string `json:"description"`
206-
Homepage string `json:"homepage"`
207-
Keywords []string `json:"keywords"`
208-
Repository interface{} `json:"repository"`
209-
Author interface{} `json:"author"`
210-
License interface{} `json:"license"`
211-
Dependencies map[string]string `json:"dependencies"`
212-
DevDependencies map[string]string `json:"devDependencies"`
213-
PeerDependencies map[string]string `json:"peerDependencies"`
214-
OptionalDependencies map[string]string `json:"optionalDependencies"`
215-
Bin interface{} `json:"bin"`
216-
}
217-
218-
func buildNpmUploadFromPackageJSON(pkgJSON []byte, tarballPath string) (*npm.PackageUpload, string, string, error) {
219-
var pkg minimalPackageJSON
220-
if err := json.Unmarshal(pkgJSON, &pkg); err != nil {
221-
return nil, "", "", fmt.Errorf("failed to parse package.json: %w", err)
222-
}
223-
224-
if pkg.Name == "" || pkg.Version == "" {
225-
return nil, "", "", fmt.Errorf("package.json must contain 'name' and 'version'")
226-
}
227-
228-
versionObj := &npm.PackageMetadataVersion{
229-
ID: pkg.Name + "@" + pkg.Version,
230-
Name: pkg.Name,
231-
Version: pkg.Version,
232-
Description: pkg.Description,
233-
Author: pkg.Author,
234-
Homepage: pkg.Homepage,
235-
License: pkg.License,
236-
Repository: pkg.Repository,
237-
Keywords: pkg.Keywords,
238-
Dependencies: pkg.Dependencies,
239-
BundleDependencies: nil,
240-
DevDependencies: pkg.DevDependencies,
241-
PeerDependencies: pkg.PeerDependencies,
242-
Bin: pkg.Bin,
243-
OptionalDependencies: pkg.OptionalDependencies,
244-
Readme: "",
245-
Dist: npm.PackageDistribution{},
246-
Maintainers: nil,
247-
}
248-
249-
metadata := npm.PackageMetadata{
250-
ID: pkg.Name,
251-
Name: pkg.Name,
252-
Description: pkg.Description,
253-
DistTags: map[string]string{
254-
"latest": pkg.Version,
255-
},
256-
Versions: map[string]*npm.PackageMetadataVersion{
257-
pkg.Version: versionObj,
258-
},
259-
Readme: "",
260-
Maintainers: nil,
261-
Time: nil,
262-
Homepage: pkg.Homepage,
263-
Keywords: pkg.Keywords,
264-
Repository: pkg.Repository,
265-
Author: pkg.Author,
266-
ReadmeFilename: "",
267-
Users: nil,
268-
License: pkg.License,
269-
}
270-
271-
// Read tarball and base64 encode it into _attachments
272-
file, err := os.Open(tarballPath)
273-
if err != nil {
274-
return nil, "", "", fmt.Errorf("failed to open tarball for attachment: %w", err)
275-
}
276-
defer file.Close()
277-
278-
data, err := io.ReadAll(file)
279-
if err != nil {
280-
return nil, "", "", fmt.Errorf("failed to read tarball for attachment: %w", err)
281-
}
282-
283-
b64Data := base64.StdEncoding.EncodeToString(data)
284-
285-
tarballName := filepath.Base(tarballPath)
286-
287-
upload := &npm.PackageUpload{
288-
PackageMetadata: metadata,
289-
Attachments: map[string]*npm.PackageAttachment{
290-
tarballName: {
291-
ContentType: "application/octet-stream",
292-
Data: b64Data,
293-
Length: len(data),
294-
},
295-
},
296-
}
297-
298-
return upload, pkg.Name, pkg.Version, nil
299-
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package utils
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"encoding/base64"
7+
"encoding/json"
8+
"fmt"
9+
"io"
10+
"path/filepath"
11+
12+
"github.com/harness/harness-cli/module/ar/migrate/types/npm"
13+
)
14+
15+
func ExtractPackageJSONFromTarball(file io.ReadCloser) ([]byte, error) {
16+
gzReader, err := gzip.NewReader(file)
17+
if err != nil {
18+
return nil, fmt.Errorf("failed to create gzip reader: %w", err)
19+
}
20+
defer gzReader.Close()
21+
22+
tarReader := tar.NewReader(gzReader)
23+
24+
for {
25+
header, err := tarReader.Next()
26+
if err == io.EOF {
27+
break
28+
}
29+
if err != nil {
30+
return nil, fmt.Errorf("failed to read tar header: %w", err)
31+
}
32+
33+
if header.FileInfo().IsDir() {
34+
continue
35+
}
36+
37+
base := filepath.Base(header.Name)
38+
if base == "package.json" {
39+
data, err := io.ReadAll(tarReader)
40+
if err != nil {
41+
return nil, fmt.Errorf("failed to read package.json from tarball: %w", err)
42+
}
43+
return data, nil
44+
}
45+
}
46+
47+
return nil, fmt.Errorf("package.json not found in tarball")
48+
}
49+
50+
// minimalPackageJSON represents the subset of fields from package.json we care about.
51+
type MinimalPackageJSON struct {
52+
Name string `json:"name"`
53+
Version string `json:"version"`
54+
Description string `json:"description"`
55+
Homepage string `json:"homepage"`
56+
Keywords []string `json:"keywords"`
57+
Repository interface{} `json:"repository"`
58+
Author interface{} `json:"author"`
59+
License interface{} `json:"license"`
60+
Dependencies map[string]string `json:"dependencies"`
61+
DevDependencies map[string]string `json:"devDependencies"`
62+
PeerDependencies map[string]string `json:"peerDependencies"`
63+
OptionalDependencies map[string]string `json:"optionalDependencies"`
64+
Bin interface{} `json:"bin"`
65+
}
66+
67+
func BuildNpmUploadFromPackageJSON(pkgJSON []byte, file io.ReadCloser) (*npm.PackageUpload, string, string, error) {
68+
var pkg MinimalPackageJSON
69+
if err := json.Unmarshal(pkgJSON, &pkg); err != nil {
70+
return nil, "", "", fmt.Errorf("failed to parse package.json: %w", err)
71+
}
72+
73+
if pkg.Name == "" || pkg.Version == "" {
74+
return nil, "", "", fmt.Errorf("package.json must contain 'name' and 'version'")
75+
}
76+
77+
versionObj := &npm.PackageMetadataVersion{
78+
ID: pkg.Name + "@" + pkg.Version,
79+
Name: pkg.Name,
80+
Version: pkg.Version,
81+
Description: pkg.Description,
82+
Author: pkg.Author,
83+
Homepage: pkg.Homepage,
84+
License: pkg.License,
85+
Repository: pkg.Repository,
86+
Keywords: pkg.Keywords,
87+
Dependencies: pkg.Dependencies,
88+
BundleDependencies: nil,
89+
DevDependencies: pkg.DevDependencies,
90+
PeerDependencies: pkg.PeerDependencies,
91+
Bin: pkg.Bin,
92+
OptionalDependencies: pkg.OptionalDependencies,
93+
Readme: "",
94+
Dist: npm.PackageDistribution{},
95+
Maintainers: nil,
96+
}
97+
98+
metadata := npm.PackageMetadata{
99+
ID: pkg.Name,
100+
Name: pkg.Name,
101+
Description: pkg.Description,
102+
DistTags: map[string]string{
103+
"latest": pkg.Version,
104+
},
105+
Versions: map[string]*npm.PackageMetadataVersion{
106+
pkg.Version: versionObj,
107+
},
108+
Readme: "",
109+
Maintainers: nil,
110+
Time: nil,
111+
Homepage: pkg.Homepage,
112+
Keywords: pkg.Keywords,
113+
Repository: pkg.Repository,
114+
Author: pkg.Author,
115+
ReadmeFilename: "",
116+
Users: nil,
117+
License: pkg.License,
118+
}
119+
120+
data, err := io.ReadAll(file)
121+
if err != nil {
122+
return nil, "", "", fmt.Errorf("failed to read tarball for attachment: %w", err)
123+
}
124+
125+
b64Data := base64.StdEncoding.EncodeToString(data)
126+
127+
// generate tarball name from package name and version
128+
tarballName := pkg.Name + "-" + pkg.Version + ".tgz"
129+
130+
upload := &npm.PackageUpload{
131+
PackageMetadata: metadata,
132+
Attachments: map[string]*npm.PackageAttachment{
133+
tarballName: {
134+
ContentType: "application/octet-stream",
135+
Data: b64Data,
136+
Length: len(data),
137+
},
138+
},
139+
}
140+
141+
return upload, pkg.Name, pkg.Version, nil
142+
}

0 commit comments

Comments
 (0)