Skip to content

Commit

Permalink
fix: CodeArtifact login should include scope
Browse files Browse the repository at this point in the history
Without the `--namespace` flag, the command forces all traffic through the CA Domain
  • Loading branch information
vincenthsh committed Jan 3, 2025
1 parent 85824a2 commit 8e9b9eb
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 19 deletions.
17 changes: 8 additions & 9 deletions plan/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,27 +471,26 @@ func (p *Plan) buildTurboRootConfig(c *v2.Config) *TurboConfig {

// Merge scopes and detect CodeArtifact registries and generate optional ca:login script
func parseJsScopes(mergedScopes *map[string]jsScope, new []jsScope) (*map[string]v2.JavascriptPackageScope, string) {
caRegistryUrls := map[string]util.CodeArtifactRepository{}
caRepos := map[string]util.CodeArtifactRepository{}
for _, newScope := range new {
(*mergedScopes)[newScope.Name] = newScope
if util.IsCodeArtifactURL(newScope.RegistryUrl) {
if _, exists := caRegistryUrls[newScope.RegistryUrl]; !exists {
caRepo, err := util.ParseRegistryUrl(newScope.RegistryUrl)
if _, exists := caRepos[newScope.Name]; !exists {
caRepo, err := util.ParseRegistryUrl(newScope.Name, newScope.RegistryUrl)
if err != nil {
logrus.Warnf("Failed to parse CodeArtifact registry URL: %s", newScope.RegistryUrl)
continue
}
caRegistryUrls[newScope.RegistryUrl] = *caRepo
caRepos[newScope.Name] = *caRepo
}
}
}
// Generate the npm ca:login script for CodeArtifact registries
caLoginScript := noCALoginRequired
if len(caRegistryUrls) > 0 {
loginSteps := make([]string, 0, len(caRegistryUrls))
for _, repo := range caRegistryUrls {
loginSteps = append(loginSteps, fmt.Sprintf("aws codeartifact login --tool npm --repository %s --domain %s --domain-owner %s --region %s",
repo.Name, repo.Domain, repo.AccountId, repo.Region))
if len(caRepos) > 0 {
loginSteps := make([]string, 0, len(caRepos))
for _, repo := range caRepos {
loginSteps = append(loginSteps, repo.LoginCommand())
}
// sort for deterministic output
sort.Strings(loginSteps)
Expand Down
9 changes: 5 additions & 4 deletions plan/ci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,10 @@ func Test_parseScopes(t *testing.T) {
r.Equal("https://domain-123456789012.d.codeartifact.us-west-2.amazonaws.com/npm/repo-name/", (*result)["@scope3"].RegistryUrl)
r.Equal("https://domain-123456789012.d.codeartifact.us-east-1.amazonaws.com/npm/repo-name/", (*result)["@scope4"].RegistryUrl)
r.Equal("https://domain-210987654321.d.codeartifact.us-west-2.amazonaws.com/npm/repo-name/", (*result)["@scope5"].RegistryUrl)
expectedScript := "aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-east-1;" +
"aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-west-2;" +
"aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 210987654321 --region us-west-2"
expectedScript := `aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-east-1 --namespace "@scope4";` +
`aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-west-2 --namespace "@scope2";` +
`aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-west-2 --namespace "@scope3";` +
`aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 210987654321 --region us-west-2 --namespace "@scope5"`
r.Equal(expectedScript, script)
})

Expand All @@ -372,6 +373,6 @@ func Test_parseScopes(t *testing.T) {
r.Len(*result, 2)
r.Equal("https://registry.npmjs.org", (*result)["@scope1"].RegistryUrl)
r.Equal("https://domain-123456789012.d.codeartifact.us-west-2.amazonaws.com/npm/repo-name/", (*result)["@scope2"].RegistryUrl)
r.Contains(script, "aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-west-2")
r.Contains(script, `aws codeartifact login --tool npm --repository repo-name --domain domain --domain-owner 123456789012 --region us-west-2 --namespace "@scope2"`)
})
}
2 changes: 1 addition & 1 deletion templates/templates/turbo/root/package.json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"ca:login": "{{ .CodeArtifactLoginScript }}",
"ca:login": {{ .CodeArtifactLoginScript | quote}},
"test": "turbo test",
"synth": "TURBO_SCM_BASE='{{ .SCMBase }}' turbo run synth --affected",
"type-check": "turbo type-check",
Expand Down
2 changes: 1 addition & 1 deletion testdata/v2_cdktf_components/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion util/codeartifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,20 @@ type CodeArtifactRepository struct {
AccountId string
Region string
Name string
Namespace string
}

// Returns the `aws codeartifact login` command for the repository with the given `scope`.
//
// refer to https://docs.aws.amazon.com/codeartifact/latest/ug/npm-auth.html#configure-npm-login-command
func (r *CodeArtifactRepository) LoginCommand() string {
return fmt.Sprintf("aws codeartifact login --tool npm --repository %s --domain %s --domain-owner %s --region %s --namespace %q",
r.Name, r.Domain, r.AccountId, r.Region, r.Namespace)
}

// Gets AWS details from the Code Artifact `registryUrl`.
// throws exception if not matching expected pattern
func ParseRegistryUrl(registryUrl string) (*CodeArtifactRepository, error) {
func ParseRegistryUrl(scope string, registryUrl string) (*CodeArtifactRepository, error) {
// https://github.com/projen/projen/blob/v0.91.4/src/javascript/util.ts#L48
regex := regexp.MustCompile(AWS_CODEARTIFACT_CAPTURE_REGEX)
matches := regex.FindStringSubmatch(registryUrl)
Expand All @@ -37,5 +46,6 @@ func ParseRegistryUrl(registryUrl string) (*CodeArtifactRepository, error) {
AccountId: matches[2],
Region: matches[3],
Name: matches[4],
Namespace: scope,
}, nil
}
10 changes: 7 additions & 3 deletions util/codeartifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@ import (
func Test_ParseRegistryUrl(t *testing.T) {
tests := []struct {
name string
scope string
url string
want *CodeArtifactRepository
wantErr bool
}{
{
name: "Valid CodeArtifact Repository URL",
url: "https://vincenthsh-123456789.d.codeartifact.ap-southeast-1.amazonaws.com/npm/npm-releases/",
name: "Valid CodeArtifact Repository URL",
scope: "@foo",
url: "https://vincenthsh-123456789.d.codeartifact.ap-southeast-1.amazonaws.com/npm/npm-releases/",
want: &CodeArtifactRepository{
Domain: "vincenthsh",
AccountId: "123456789",
Region: "ap-southeast-1",
Name: "npm-releases",
Namespace: "@foo",
},
wantErr: false,
},
{
name: "Invalid CodeArtifact URL",
scope: "@foo",
url: "https://example.com",
want: nil,
wantErr: true,
Expand All @@ -33,7 +37,7 @@ func Test_ParseRegistryUrl(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseRegistryUrl(tt.url)
got, err := ParseRegistryUrl(tt.scope, tt.url)
if (err != nil) != tt.wantErr {
t.Errorf("parseRegistryUrl() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down

0 comments on commit 8e9b9eb

Please sign in to comment.