Skip to content

Commit

Permalink
feat: adds cue release (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgilman authored Dec 5, 2024
1 parent 2f078ed commit 6d37883
Show file tree
Hide file tree
Showing 18 changed files with 1,240 additions and 30 deletions.
32 changes: 30 additions & 2 deletions actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
18 changes: 14 additions & 4 deletions blueprint.cue
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@ global: {
]
providers: {
aws: {
region: "eu-central-1"
registry: "332405224602.dkr.ecr.eu-central-1.amazonaws.com"
role: "arn:aws:iam::332405224602:role/ci"
ecr: {
autoCreate: true
registry: "332405224602.dkr.ecr.eu-central-1.amazonaws.com"
}
region: "eu-central-1"
role: "arn:aws:iam::332405224602:role/ci"
}

cue: {
install: true
registry: aws.ecr.registry
registryPrefix: "cue"
version: "0.11.0"
}

docker: credentials: {
Expand Down Expand Up @@ -55,7 +65,7 @@ global: {
]
}
deployment: {
registry: ci.providers.aws.registry
registry: ci.providers.aws.ecr.registry
repo: {
url: "https://github.com/input-output-hk/catalyst-world"
ref: "master"
Expand Down
12 changes: 7 additions & 5 deletions cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ go 1.23.0
require (
cuelang.org/go v0.10.0
github.com/alecthomas/kong v0.9.0
github.com/aws/aws-sdk-go-v2 v1.32.6
github.com/aws/aws-sdk-go-v2/config v1.27.40
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/lipgloss v0.13.0
Expand All @@ -30,20 +33,18 @@ require (
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230219212500-1f9a474cc2dc // indirect
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.40 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.38 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4 // indirect
github.com/aws/smithy-go v1.21.0 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/x/ansi v0.2.3 // indirect
github.com/charmbracelet/x/term v0.2.0 // indirect
Expand All @@ -64,6 +65,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand Down
22 changes: 14 additions & 8 deletions cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4=
github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/config v1.27.40 h1:sie4mPBGFOO+Z27+yHzvyN31G20h/bf2xb5mCbpLv2Q=
github.com/aws/aws-sdk-go-v2/config v1.27.40/go.mod h1:4KW7Aa5tNo+0VHnuLnnE1vPHtwMurlNZNS65IdcewHA=
github.com/aws/aws-sdk-go-v2/credentials v1.17.38 h1:iM90eRhCeZtlkzCNCG1JysOzJXGYf5rx80aD1lUgNDU=
github.com/aws/aws-sdk-go-v2/credentials v1.17.38/go.mod h1:TCVYPZeQuLaYNEkf/TVn6k5k/zdVZZ7xH9po548VNNg=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 h1:R+5XKIJga2K9Dkj0/iQ6fD/MBGo02oxGGFTc512lK/Q=
github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7/go.mod h1:fDPQV/6ONOQOjvtKhtypIy1wcGLcKYtoK/lvZ9fyDGQ=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
Expand All @@ -51,8 +53,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4 h1:4f2/JKYZHAZbQ7koBpZ012bK
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.4/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4 h1:uK6dUUdJtqutK1XO/tmNaQMJiPLCJY/eAeOOmqQ6ygY=
github.com/aws/aws-sdk-go-v2/service/sts v1.31.4/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
Expand Down Expand Up @@ -131,6 +133,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
Expand Down Expand Up @@ -293,6 +297,8 @@ gopkg.in/jfontan/go-billy-desfacer.v0 v0.0.0-20210209210102-b43512b1cad0/go.mod
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15 changes: 14 additions & 1 deletion cli/pkg/executor/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ type LocalExecutor struct {
redirect bool
stderrStream io.Writer
stdoutStream io.Writer
workdir string
}

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

if e.workdir != "" {
cmd.Dir = e.workdir
}

e.logger.Debug("Executing local command", "command", cmd.String(), "workdir", cmd.Dir)
if e.redirect {
var buffer bytes.Buffer
errChan := make(chan error, 2)
Expand Down Expand Up @@ -100,6 +105,14 @@ func WithRedirectTo(stdout, stderr io.Writer) LocalExecutorOption {
}
}

// WithWorkdir is an option that configures the LocalExecutor to run commands in
// the given working directory.
func WithWorkdir(workdir string) LocalExecutorOption {
return func(e *LocalExecutor) {
e.workdir = workdir
}
}

type WrappedLocalExecutor struct {
Executor
command string
Expand Down
18 changes: 18 additions & 0 deletions cli/pkg/providers/aws/common.go
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
}
146 changes: 146 additions & 0 deletions cli/pkg/providers/aws/ecr.go
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)
}
Loading

0 comments on commit 6d37883

Please sign in to comment.