Skip to content

Commit 6d37883

Browse files
authored
feat: adds cue release (#104)
1 parent 2f078ed commit 6d37883

File tree

18 files changed

+1240
-30
lines changed

18 files changed

+1240
-30
lines changed

actions/setup/action.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ inputs:
99
description: If true, skip authenticating with AWS and configuring ECR
1010
required: false
1111
default: "false"
12+
skip_cue:
13+
description: If true, skips installing CUE CLI if the provider is configured
14+
required: false
15+
default: "false"
1216
skip_docker:
1317
description: If true, skip authenticating to DockerHub
1418
skip_earthly:
@@ -40,7 +44,7 @@ runs:
4044
AWS=$(echo "$BP" | jq -r .global.ci.providers.aws)
4145
if [[ "$AWS" != "null" ]]; then
4246
REGION=$(echo "$BP" | jq -r .global.ci.providers.aws.region)
43-
REGISTRY=$(echo "$BP" | jq -r .global.ci.providers.aws.registry)
47+
REGISTRY=$(echo "$BP" | jq -r .global.ci.providers.aws.ecr.registry)
4448
ROLE=$(echo "$BP" | jq -r .global.ci.providers.aws.role)
4549
4650
echo "region=$REGION" >> $GITHUB_OUTPUT
@@ -197,4 +201,28 @@ runs:
197201
uses: stefanprodan/timoni/actions/setup@main
198202
if: steps.timoni.outputs.install && steps.timoni.conclusion == 'success'
199203
with:
200-
version: ${{ steps.timoni.outputs.version }}
204+
version: ${{ steps.timoni.outputs.version }}
205+
206+
# CUE Provider
207+
- name: Get CUE provider configuration
208+
id: cue
209+
if: inputs.skip_cue == 'false'
210+
shell: bash
211+
run: |
212+
echo "==== CUE Setup ====="
213+
BP=$(forge dump .)
214+
215+
CUE=$(echo "$BP" | jq -r .global.ci.providers.cue.install)
216+
if [[ "$CUE" == "true" ]]; then
217+
INSTALL=1
218+
VERSION=$(echo "$BP" | jq -r .global.ci.providers.cue.version)
219+
echo "install=$INSTALL" >> $GITHUB_OUTPUT
220+
echo "version=$VERSION" >> $GITHUB_OUTPUT
221+
else
222+
echo "Not installing CUE CLI"
223+
fi
224+
- name: Install CUE
225+
uses: cue-lang/[email protected]
226+
if: steps.cue.outputs.install && steps.cue.conclusion == 'success'
227+
with:
228+
version: v${{ steps.cue.outputs.version }}

blueprint.cue

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@ global: {
1212
]
1313
providers: {
1414
aws: {
15-
region: "eu-central-1"
16-
registry: "332405224602.dkr.ecr.eu-central-1.amazonaws.com"
17-
role: "arn:aws:iam::332405224602:role/ci"
15+
ecr: {
16+
autoCreate: true
17+
registry: "332405224602.dkr.ecr.eu-central-1.amazonaws.com"
18+
}
19+
region: "eu-central-1"
20+
role: "arn:aws:iam::332405224602:role/ci"
21+
}
22+
23+
cue: {
24+
install: true
25+
registry: aws.ecr.registry
26+
registryPrefix: "cue"
27+
version: "0.11.0"
1828
}
1929

2030
docker: credentials: {
@@ -55,7 +65,7 @@ global: {
5565
]
5666
}
5767
deployment: {
58-
registry: ci.providers.aws.registry
68+
registry: ci.providers.aws.ecr.registry
5969
repo: {
6070
url: "https://github.com/input-output-hk/catalyst-world"
6171
ref: "master"

cli/go.mod

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ go 1.23.0
55
require (
66
cuelang.org/go v0.10.0
77
github.com/alecthomas/kong v0.9.0
8+
github.com/aws/aws-sdk-go-v2 v1.32.6
9+
github.com/aws/aws-sdk-go-v2/config v1.27.40
10+
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7
811
github.com/charmbracelet/bubbles v0.20.0
912
github.com/charmbracelet/bubbletea v1.1.1
1013
github.com/charmbracelet/lipgloss v0.13.0
@@ -30,20 +33,18 @@ require (
3033
github.com/ProtonMail/go-crypto v1.0.0 // indirect
3134
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230219212500-1f9a474cc2dc // indirect
3235
github.com/aws/aws-sdk-go v1.55.5 // indirect
33-
github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect
34-
github.com/aws/aws-sdk-go-v2/config v1.27.40 // indirect
3536
github.com/aws/aws-sdk-go-v2/credentials v1.17.38 // indirect
3637
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
37-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
38-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
38+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
39+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
3940
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
4041
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
4142
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
4243
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.4 // indirect
4344
github.com/aws/aws-sdk-go-v2/service/sso v1.23.4 // indirect
4445
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4 // indirect
4546
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4 // indirect
46-
github.com/aws/smithy-go v1.21.0 // indirect
47+
github.com/aws/smithy-go v1.22.1 // indirect
4748
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
4849
github.com/charmbracelet/x/ansi v0.2.3 // indirect
4950
github.com/charmbracelet/x/term v0.2.0 // indirect
@@ -64,6 +65,7 @@ require (
6465
github.com/hashicorp/errwrap v1.1.0 // indirect
6566
github.com/hashicorp/go-multierror v1.1.1 // indirect
6667
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
68+
github.com/jmespath/go-jmespath v0.4.0 // indirect
6769
github.com/kevinburke/ssh_config v1.2.0 // indirect
6870
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
6971
github.com/mattn/go-isatty v0.0.20 // indirect

cli/go.sum

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,22 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
2525
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
2626
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
2727
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
28-
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
29-
github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
28+
github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4=
29+
github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
3030
github.com/aws/aws-sdk-go-v2/config v1.27.40 h1:sie4mPBGFOO+Z27+yHzvyN31G20h/bf2xb5mCbpLv2Q=
3131
github.com/aws/aws-sdk-go-v2/config v1.27.40/go.mod h1:4KW7Aa5tNo+0VHnuLnnE1vPHtwMurlNZNS65IdcewHA=
3232
github.com/aws/aws-sdk-go-v2/credentials v1.17.38 h1:iM90eRhCeZtlkzCNCG1JysOzJXGYf5rx80aD1lUgNDU=
3333
github.com/aws/aws-sdk-go-v2/credentials v1.17.38/go.mod h1:TCVYPZeQuLaYNEkf/TVn6k5k/zdVZZ7xH9po548VNNg=
3434
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
3535
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
36-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
37-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
38-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
39-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
36+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU=
37+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU=
38+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA=
39+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE=
4040
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
4141
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
42+
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 h1:R+5XKIJga2K9Dkj0/iQ6fD/MBGo02oxGGFTc512lK/Q=
43+
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7/go.mod h1:fDPQV/6ONOQOjvtKhtypIy1wcGLcKYtoK/lvZ9fyDGQ=
4244
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
4345
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
4446
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
@@ -51,8 +53,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4 h1:4f2/JKYZHAZbQ7koBpZ012bK
5153
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
5254
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4 h1:uK6dUUdJtqutK1XO/tmNaQMJiPLCJY/eAeOOmqQ6ygY=
5355
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
54-
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
55-
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
56+
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
57+
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
5658
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
5759
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
5860
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
@@ -131,6 +133,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
131133
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
132134
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
133135
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
136+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
137+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
134138
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
135139
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
136140
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -293,6 +297,8 @@ gopkg.in/jfontan/go-billy-desfacer.v0 v0.0.0-20210209210102-b43512b1cad0/go.mod
293297
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
294298
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
295299
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
300+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
301+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
296302
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
297303
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
298304
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

cli/pkg/executor/local.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ type LocalExecutor struct {
1717
redirect bool
1818
stderrStream io.Writer
1919
stdoutStream io.Writer
20+
workdir string
2021
}
2122

2223
func (e *LocalExecutor) Execute(command string, args ...string) ([]byte, error) {
2324
cmd := exec.Command(command, args...)
24-
e.logger.Debug("Executing local command", "command", cmd.String())
2525

26+
if e.workdir != "" {
27+
cmd.Dir = e.workdir
28+
}
29+
30+
e.logger.Debug("Executing local command", "command", cmd.String(), "workdir", cmd.Dir)
2631
if e.redirect {
2732
var buffer bytes.Buffer
2833
errChan := make(chan error, 2)
@@ -100,6 +105,14 @@ func WithRedirectTo(stdout, stderr io.Writer) LocalExecutorOption {
100105
}
101106
}
102107

108+
// WithWorkdir is an option that configures the LocalExecutor to run commands in
109+
// the given working directory.
110+
func WithWorkdir(workdir string) LocalExecutorOption {
111+
return func(e *LocalExecutor) {
112+
e.workdir = workdir
113+
}
114+
}
115+
103116
type WrappedLocalExecutor struct {
104117
Executor
105118
command string

cli/pkg/providers/aws/common.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package aws
2+
3+
import (
4+
"context"
5+
6+
"github.com/aws/aws-sdk-go-v2/aws"
7+
"github.com/aws/aws-sdk-go-v2/config"
8+
)
9+
10+
// NewConfig returns a new AWS configuration.
11+
func NewConfig() (aws.Config, error) {
12+
cfg, err := config.LoadDefaultConfig(context.TODO())
13+
if err != nil {
14+
return aws.Config{}, err
15+
}
16+
17+
return cfg, nil
18+
}

cli/pkg/providers/aws/ecr.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package aws
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"regexp"
8+
"strings"
9+
10+
"github.com/aws/aws-sdk-go-v2/service/ecr"
11+
"github.com/aws/aws-sdk-go-v2/service/ecr/types"
12+
"github.com/aws/aws-sdk-go/aws"
13+
"github.com/input-output-hk/catalyst-forge/lib/project/project"
14+
)
15+
16+
//go:generate go run github.com/matryer/moq@latest -skip-ensure --pkg mocks -out mocks/ecr.go . AWSECRClient
17+
18+
// AWSECRClient is an interface for an AWS ECR client.
19+
type AWSECRClient interface {
20+
CreateRepository(ctx context.Context, params *ecr.CreateRepositoryInput, optFns ...func(*ecr.Options)) (*ecr.CreateRepositoryOutput, error)
21+
DescribeRepositories(ctx context.Context, params *ecr.DescribeRepositoriesInput, optFns ...func(*ecr.Options)) (*ecr.DescribeRepositoriesOutput, error)
22+
}
23+
24+
// ECRClient is a client for interacting with AWS ECR.
25+
type ECRClient struct {
26+
client AWSECRClient
27+
logger *slog.Logger
28+
}
29+
30+
// CreateECRRepository creates a new ECR repository.
31+
// By default, the repository is immutable and has image scanning enabled.
32+
// The repository is also tagged with metadata about the given project.
33+
func (c *ECRClient) CreateECRRepository(project *project.Project, name string) error {
34+
input := &ecr.CreateRepositoryInput{
35+
RepositoryName: aws.String(name),
36+
ImageTagMutability: types.ImageTagMutabilityImmutable,
37+
ImageScanningConfiguration: &types.ImageScanningConfiguration{
38+
ScanOnPush: true,
39+
},
40+
EncryptionConfiguration: &types.EncryptionConfiguration{
41+
EncryptionType: types.EncryptionTypeAes256,
42+
},
43+
Tags: []types.Tag{
44+
{
45+
Key: aws.String("BuiltWith"),
46+
Value: aws.String("Catalyst Forge"),
47+
},
48+
{
49+
Key: aws.String("Repo"),
50+
Value: aws.String(project.Blueprint.Global.Repo.Name),
51+
},
52+
{
53+
Key: aws.String("RepoPath"),
54+
Value: aws.String(project.Path),
55+
},
56+
},
57+
}
58+
59+
_, err := c.client.CreateRepository(context.TODO(), input)
60+
if err != nil {
61+
return err
62+
}
63+
64+
return nil
65+
}
66+
67+
// ECRRepoExists checks whether an ECR repository exists.
68+
func (c *ECRClient) ECRRepoExists(name string) (bool, error) {
69+
_, err := c.client.DescribeRepositories(context.Background(), &ecr.DescribeRepositoriesInput{
70+
RepositoryNames: []string{name},
71+
})
72+
if err != nil {
73+
if strings.Contains(err.Error(), "RepositoryNotFoundException") || strings.Contains(err.Error(), "not found") {
74+
return false, nil
75+
} else {
76+
return false, err
77+
}
78+
}
79+
80+
return true, nil
81+
}
82+
83+
// NewECRClient returns a new ECR client.
84+
func NewECRClient(logger *slog.Logger) (ECRClient, error) {
85+
cfg, err := NewConfig()
86+
if err != nil {
87+
return ECRClient{}, err
88+
}
89+
90+
c := ecr.NewFromConfig(cfg)
91+
92+
return ECRClient{
93+
client: c,
94+
logger: logger,
95+
}, nil
96+
}
97+
98+
// NewCustomECRClient returns a new ECR client with a custom client.
99+
func NewCustomECRClient(client AWSECRClient, logger *slog.Logger) ECRClient {
100+
return ECRClient{
101+
client: client,
102+
logger: logger,
103+
}
104+
}
105+
106+
// ExtractECRRepoName extracts the repository name from an ECR URI.
107+
func ExtractECRRepoName(ecrURI string) (string, error) {
108+
if strings.Contains(ecrURI, "://") {
109+
parts := strings.SplitN(ecrURI, "://", 2)
110+
ecrURI = parts[1]
111+
}
112+
113+
parts := strings.SplitN(ecrURI, "/", 2)
114+
if len(parts) != 2 {
115+
return "", fmt.Errorf("invalid ECR URI format: no repository path found")
116+
}
117+
118+
repoPath := parts[1]
119+
if strings.Contains(repoPath, "@sha256:") {
120+
repoPath = strings.Split(repoPath, "@sha256:")[0]
121+
}
122+
if strings.Contains(repoPath, ":") {
123+
repoPath = strings.Split(repoPath, ":")[0]
124+
}
125+
126+
return repoPath, nil
127+
}
128+
129+
// IsECRAddress returns whether the given address is an ECR address.
130+
func IsECRRegistry(registryURL string) bool {
131+
// Match pattern: <account>.dkr.ecr.<region>.amazonaws.com
132+
// where account is 12 digits and region is a valid AWS region format
133+
ecrPattern := regexp.MustCompile(`^\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com`)
134+
135+
cleanURL := registryURL
136+
if strings.Contains(cleanURL, "://") {
137+
parts := strings.SplitN(cleanURL, "://", 2)
138+
cleanURL = parts[1]
139+
}
140+
141+
if strings.Contains(cleanURL, "/") {
142+
cleanURL = strings.SplitN(cleanURL, "/", 2)[0]
143+
}
144+
145+
return ecrPattern.MatchString(cleanURL)
146+
}

0 commit comments

Comments
 (0)