-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,240 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,10 @@ inputs: | |
description: If true, skip authenticating with AWS and configuring ECR | ||
required: false | ||
default: "false" | ||
skip_cue: | ||
description: If true, skips installing CUE CLI if the provider is configured | ||
required: false | ||
default: "false" | ||
skip_docker: | ||
description: If true, skip authenticating to DockerHub | ||
skip_earthly: | ||
|
@@ -40,7 +44,7 @@ runs: | |
AWS=$(echo "$BP" | jq -r .global.ci.providers.aws) | ||
if [[ "$AWS" != "null" ]]; then | ||
REGION=$(echo "$BP" | jq -r .global.ci.providers.aws.region) | ||
REGISTRY=$(echo "$BP" | jq -r .global.ci.providers.aws.registry) | ||
REGISTRY=$(echo "$BP" | jq -r .global.ci.providers.aws.ecr.registry) | ||
ROLE=$(echo "$BP" | jq -r .global.ci.providers.aws.role) | ||
echo "region=$REGION" >> $GITHUB_OUTPUT | ||
|
@@ -197,4 +201,28 @@ runs: | |
uses: stefanprodan/timoni/actions/setup@main | ||
if: steps.timoni.outputs.install && steps.timoni.conclusion == 'success' | ||
with: | ||
version: ${{ steps.timoni.outputs.version }} | ||
version: ${{ steps.timoni.outputs.version }} | ||
|
||
# CUE Provider | ||
- name: Get CUE provider configuration | ||
id: cue | ||
if: inputs.skip_cue == 'false' | ||
shell: bash | ||
run: | | ||
echo "==== CUE Setup =====" | ||
BP=$(forge dump .) | ||
CUE=$(echo "$BP" | jq -r .global.ci.providers.cue.install) | ||
if [[ "$CUE" == "true" ]]; then | ||
INSTALL=1 | ||
VERSION=$(echo "$BP" | jq -r .global.ci.providers.cue.version) | ||
echo "install=$INSTALL" >> $GITHUB_OUTPUT | ||
echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
else | ||
echo "Not installing CUE CLI" | ||
fi | ||
- name: Install CUE | ||
uses: cue-lang/[email protected] | ||
if: steps.cue.outputs.install && steps.cue.conclusion == 'success' | ||
with: | ||
version: v${{ steps.cue.outputs.version }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package aws | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go-v2/config" | ||
) | ||
|
||
// NewConfig returns a new AWS configuration. | ||
func NewConfig() (aws.Config, error) { | ||
cfg, err := config.LoadDefaultConfig(context.TODO()) | ||
if err != nil { | ||
return aws.Config{}, err | ||
} | ||
|
||
return cfg, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package aws | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log/slog" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go-v2/service/ecr" | ||
"github.com/aws/aws-sdk-go-v2/service/ecr/types" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/input-output-hk/catalyst-forge/lib/project/project" | ||
) | ||
|
||
//go:generate go run github.com/matryer/moq@latest -skip-ensure --pkg mocks -out mocks/ecr.go . AWSECRClient | ||
|
||
// AWSECRClient is an interface for an AWS ECR client. | ||
type AWSECRClient interface { | ||
CreateRepository(ctx context.Context, params *ecr.CreateRepositoryInput, optFns ...func(*ecr.Options)) (*ecr.CreateRepositoryOutput, error) | ||
DescribeRepositories(ctx context.Context, params *ecr.DescribeRepositoriesInput, optFns ...func(*ecr.Options)) (*ecr.DescribeRepositoriesOutput, error) | ||
} | ||
|
||
// ECRClient is a client for interacting with AWS ECR. | ||
type ECRClient struct { | ||
client AWSECRClient | ||
logger *slog.Logger | ||
} | ||
|
||
// CreateECRRepository creates a new ECR repository. | ||
// By default, the repository is immutable and has image scanning enabled. | ||
// The repository is also tagged with metadata about the given project. | ||
func (c *ECRClient) CreateECRRepository(project *project.Project, name string) error { | ||
input := &ecr.CreateRepositoryInput{ | ||
RepositoryName: aws.String(name), | ||
ImageTagMutability: types.ImageTagMutabilityImmutable, | ||
ImageScanningConfiguration: &types.ImageScanningConfiguration{ | ||
ScanOnPush: true, | ||
}, | ||
EncryptionConfiguration: &types.EncryptionConfiguration{ | ||
EncryptionType: types.EncryptionTypeAes256, | ||
}, | ||
Tags: []types.Tag{ | ||
{ | ||
Key: aws.String("BuiltWith"), | ||
Value: aws.String("Catalyst Forge"), | ||
}, | ||
{ | ||
Key: aws.String("Repo"), | ||
Value: aws.String(project.Blueprint.Global.Repo.Name), | ||
}, | ||
{ | ||
Key: aws.String("RepoPath"), | ||
Value: aws.String(project.Path), | ||
}, | ||
}, | ||
} | ||
|
||
_, err := c.client.CreateRepository(context.TODO(), input) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// ECRRepoExists checks whether an ECR repository exists. | ||
func (c *ECRClient) ECRRepoExists(name string) (bool, error) { | ||
_, err := c.client.DescribeRepositories(context.Background(), &ecr.DescribeRepositoriesInput{ | ||
RepositoryNames: []string{name}, | ||
}) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "RepositoryNotFoundException") || strings.Contains(err.Error(), "not found") { | ||
return false, nil | ||
} else { | ||
return false, err | ||
} | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
// NewECRClient returns a new ECR client. | ||
func NewECRClient(logger *slog.Logger) (ECRClient, error) { | ||
cfg, err := NewConfig() | ||
if err != nil { | ||
return ECRClient{}, err | ||
} | ||
|
||
c := ecr.NewFromConfig(cfg) | ||
|
||
return ECRClient{ | ||
client: c, | ||
logger: logger, | ||
}, nil | ||
} | ||
|
||
// NewCustomECRClient returns a new ECR client with a custom client. | ||
func NewCustomECRClient(client AWSECRClient, logger *slog.Logger) ECRClient { | ||
return ECRClient{ | ||
client: client, | ||
logger: logger, | ||
} | ||
} | ||
|
||
// ExtractECRRepoName extracts the repository name from an ECR URI. | ||
func ExtractECRRepoName(ecrURI string) (string, error) { | ||
if strings.Contains(ecrURI, "://") { | ||
parts := strings.SplitN(ecrURI, "://", 2) | ||
ecrURI = parts[1] | ||
} | ||
|
||
parts := strings.SplitN(ecrURI, "/", 2) | ||
if len(parts) != 2 { | ||
return "", fmt.Errorf("invalid ECR URI format: no repository path found") | ||
} | ||
|
||
repoPath := parts[1] | ||
if strings.Contains(repoPath, "@sha256:") { | ||
repoPath = strings.Split(repoPath, "@sha256:")[0] | ||
} | ||
if strings.Contains(repoPath, ":") { | ||
repoPath = strings.Split(repoPath, ":")[0] | ||
} | ||
|
||
return repoPath, nil | ||
} | ||
|
||
// IsECRAddress returns whether the given address is an ECR address. | ||
func IsECRRegistry(registryURL string) bool { | ||
// Match pattern: <account>.dkr.ecr.<region>.amazonaws.com | ||
// where account is 12 digits and region is a valid AWS region format | ||
ecrPattern := regexp.MustCompile(`^\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com`) | ||
|
||
cleanURL := registryURL | ||
if strings.Contains(cleanURL, "://") { | ||
parts := strings.SplitN(cleanURL, "://", 2) | ||
cleanURL = parts[1] | ||
} | ||
|
||
if strings.Contains(cleanURL, "/") { | ||
cleanURL = strings.SplitN(cleanURL, "/", 2)[0] | ||
} | ||
|
||
return ecrPattern.MatchString(cleanURL) | ||
} |
Oops, something went wrong.