Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
26b8410
test charm lib impelentation for cre init
ejacquier Jan 30, 2026
4a7699d
Add shared UI package with Charm ecosystem for styled CLI output
ejacquier Jan 30, 2026
c37b25c
Add spinner to cre init and fix layout issues
ejacquier Jan 30, 2026
26ec16a
Style help page with Lipgloss
ejacquier Jan 30, 2026
46d61de
Apply Chainlink Blocks color palette to CLI
ejacquier Jan 30, 2026
fbfda62
Modernize cre login command with Charm UI components
ejacquier Jan 30, 2026
6ebd121
Add styled error display for CLI using ui.Error()
ejacquier Jan 30, 2026
3d819a4
Improve CLI error display with styled output and smart usage handling
ejacquier Jan 30, 2026
744e5de
Add login prompt when authentication is required
ejacquier Jan 30, 2026
ebe0091
Fix tests broken by Charm UI changes
ejacquier Jan 30, 2026
eb5a70e
Fix cre logout to skip credential check and handle nil credentials
ejacquier Jan 30, 2026
3b0f685
Modernize cre version command with Charm UI styling
ejacquier Jan 30, 2026
137594c
Modernize cre update command with progress bar and Charm UI
ejacquier Jan 30, 2026
f56168f
Fix creinit tests to provide all inputs via flags
ejacquier Jan 30, 2026
214ed37
Update the list-key command to use the shared Charm UI components for
ejacquier Jan 30, 2026
f2ba1ae
Update test assertions for Charm UI output changes
ejacquier Jan 30, 2026
dc83bff
Update the generate-bindings command to use the shared Charm UI
ejacquier Jan 30, 2026
4dd218d
Update the secrets list command to use the shared Charm UI component
ejacquier Jan 30, 2026
a6dde07
Update the secrets delete command to use the shared Charm UI components
ejacquier Jan 30, 2026
ac70c74
Modernize workflow pause, activate, delete with Charm UI
ejacquier Jan 30, 2026
277e84b
updated styling for workflow deploy, pause, delete and transaction
ejacquier Jan 31, 2026
d76bdc3
mondernized link and unlink command using charm lib
ejacquier Jan 31, 2026
30ecbde
updated missing charm console output in creinit.go
ejacquier Jan 31, 2026
c32a002
fixed build output ui
ejacquier Jan 31, 2026
932908c
updated fmt.print with new ui that were not replaced
ejacquier Jan 31, 2026
182ae41
mondernized simulate command with charm ui output
ejacquier Jan 31, 2026
620e035
1. Updated internal/settings/settings_generate.go:
ejacquier Jan 31, 2026
6daa25a
improved prompt behavior with autocomplete and default value
ejacquier Jan 31, 2026
81bbfc9
added error helpers
ejacquier Jan 31, 2026
bee55e8
improved login command with cancellation / escape
ejacquier Jan 31, 2026
1856e4e
fixed logout issue that was not flushing credentials
ejacquier Jan 31, 2026
2b6a6f6
updated login to remove the esc logic
ejacquier Jan 31, 2026
e8c8cb8
updated secret tests that were failing
ejacquier Jan 31, 2026
2d89b8d
fixed lint error: unused functions and returns
ejacquier Feb 2, 2026
0678640
Refactored credentials.go for deploy access status and added deploy a…
ejacquier Jan 30, 2026
9981c7b
Updated gated message with the command to request access
ejacquier Jan 30, 2026
42a5c48
added new account access command
ejacquier Jan 30, 2026
ff49d9a
added account access command to settings exclusion
ejacquier Jan 30, 2026
6ca89a8
access command logic to submit form to zendesk
ejacquier Jan 30, 2026
0882d73
added prompt to request access when running cre account access cmd
ejacquier Jan 30, 2026
1a5eb0a
Refactor access request logic into shared package and add deploy acce…
ejacquier Jan 30, 2026
6222ef6
Fix background goroutine error appearing during deploy access prompt
ejacquier Jan 30, 2026
4af0fa9
Update account command description to mention deploy access
ejacquier Jan 30, 2026
6d74f56
Show deploy access hint after successful workflow simulation
ejacquier Jan 30, 2026
5a24a1c
Add deploy access hint to global help template for gated users
ejacquier Jan 30, 2026
80834e9
Added prompt to describe use cases when request access request
ejacquier Jan 30, 2026
ef2d10c
update code so that request is sent to a proxy that will take care of…
ejacquier Jan 30, 2026
1a26d58
updated deploy request changes to be compatible with new charm lib re…
ejacquier Feb 2, 2026
4153aef
updated simulator deploy message to use box layout
ejacquier Feb 2, 2026
1b261c9
Yes is now selected by default for cre deploy access request prompt
ejacquier Feb 2, 2026
1f992ad
updated creinit to use bubbletea wizard
ejacquier Feb 3, 2026
6ec58c1
fixed linter issues
ejacquier Feb 4, 2026
0a0b4c0
Merge branch 'main' into feature/charm-lib-refactoring
ejacquier Feb 4, 2026
29eb300
fixed weird spacing in creinit.go
ejacquier Feb 4, 2026
7fbd64d
cre init: wizard now display files created in <workflow directory> and
ejacquier Feb 4, 2026
3a785ef
restored original comments in creinit.go
ejacquier Feb 4, 2026
9a3af2c
fixed update cmd progress indicator issue
ejacquier Feb 4, 2026
df51dfa
Merge branch 'feature/charm-lib-refactoring' into feature/DEVSVCS-372…
ejacquier Feb 4, 2026
16a2c0c
Temp mock access request behavior before API implementation
ejacquier Feb 4, 2026
d99f261
Added ascii logo in creinit wizard
ejacquier Feb 4, 2026
72eceef
Merge branch 'feature/charm-lib-refactoring' into feature/DEVSVCS-372…
ejacquier Feb 4, 2026
4fc6d6e
Spinner do not display in verbose mode to avoid stdout and stderr con…
ejacquier Feb 5, 2026
516444c
Merge branch 'feature/charm-lib-refactoring' into feature/DEVSVCS-372…
ejacquier Feb 6, 2026
96a477f
replaced temp REST HTTP client with GraphQL client
ejacquier Feb 6, 2026
83f2225
added tests for access cmd
ejacquier Feb 6, 2026
d906448
added qa developer runbook and report template
ejacquier Feb 12, 2026
95a1f33
Refactored prompt into one central ui lib: internal/ui/prompts.go — W…
ejacquier Feb 12, 2026
2473f83
Merge branch 'main' into feature/charm-lib-refactoring
ejacquier Feb 12, 2026
4dc7e8b
fixed lint and go.mod issues
ejacquier Feb 12, 2026
776e73d
internal/ui/output.go — Error/warning output to stderr instead of std…
ejacquier Feb 13, 2026
2998fcb
updated tests
ejacquier Feb 13, 2026
e476470
Merge branch 'main' into feature/charm-lib-refactoring
ejacquier Feb 16, 2026
033bbd8
removed runbook from this branch
ejacquier Feb 16, 2026
86b4888
Merge branch 'feature/charm-lib-refactoring' of github.com:smartcontr…
ejacquier Feb 16, 2026
5a75b30
Update cmd/whoami/whoami.go
ejacquier Feb 16, 2026
6671920
Merge branch 'feature/charm-lib-refactoring' into feature/DEVSVCS-372…
ejacquier Feb 16, 2026
9f8448c
merge main branch and fix conflicts
ejacquier Feb 17, 2026
7ea814e
Added 1500char limit for request access description
ejacquier Feb 18, 2026
fd23edf
gendoc
ejacquier Feb 18, 2026
2cbd916
fixed tests
ejacquier Feb 18, 2026
f1a0714
fixed tests
ejacquier Feb 18, 2026
e412ccb
fixed lint error
ejacquier Feb 18, 2026
d563801
Check for trim space
ejacquier Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions cmd/account/access/access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package access

import (
"context"
"fmt"

"github.com/rs/zerolog"
"github.com/spf13/cobra"

"github.com/smartcontractkit/cre-cli/internal/accessrequest"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/ui"
)

func New(runtimeCtx *runtime.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "access",
Short: "Check or request deployment access",
Long: "Check your deployment access status or request access to deploy workflows.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
h := NewHandler(runtimeCtx)
return h.Execute(cmd.Context())
},
}

return cmd
}

type Handler struct {
log *zerolog.Logger
credentials *credentials.Credentials
requester *accessrequest.Requester
}

func NewHandler(ctx *runtime.Context) *Handler {
return &Handler{
log: ctx.Logger,
credentials: ctx.Credentials,
requester: accessrequest.NewRequester(ctx.Credentials, ctx.EnvironmentSet, ctx.Logger),
}
}

func (h *Handler) Execute(ctx context.Context) error {
deployAccess, err := h.credentials.GetDeploymentAccessStatus()
if err != nil {
return fmt.Errorf("failed to check deployment access: %w", err)
}

if deployAccess.HasAccess {
ui.Line()
ui.Success("You have deployment access enabled for your organization.")
ui.Line()
ui.Print("You're all set to deploy workflows. Get started with:")
ui.Line()
ui.Command(" cre workflow deploy")
ui.Line()
ui.Dim("For more information, run 'cre workflow deploy --help'")
ui.Line()
return nil
}

return h.requester.PromptAndSubmitRequest(ctx)
}
85 changes: 85 additions & 0 deletions cmd/account/access/access_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package access_test

import (
"context"
"io"
"os"
"strings"
"testing"

"github.com/rs/zerolog"

"github.com/smartcontractkit/cre-cli/cmd/account/access"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/runtime"
)

func TestHandlerExecute_HasAccess(t *testing.T) {
// API key auth type always returns HasAccess: true
creds := &credentials.Credentials{
AuthType: "api-key",
APIKey: "test-key",
}
logger := zerolog.New(io.Discard)
envSet := &environments.EnvironmentSet{}

rtCtx := &runtime.Context{
Credentials: creds,
Logger: &logger,
EnvironmentSet: envSet,
}

// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

h := access.NewHandler(rtCtx)
err := h.Execute(context.Background())

w.Close()
os.Stdout = oldStdout
var output strings.Builder
_, _ = io.Copy(&output, r)

if err != nil {
t.Fatalf("unexpected error: %v", err)
}

out := output.String()
expectedSnippets := []string{
"deployment access enabled",
"cre workflow deploy",
}
for _, snippet := range expectedSnippets {
if !strings.Contains(out, snippet) {
t.Errorf("output missing %q; full output:\n%s", snippet, out)
}
}
}

func TestHandlerExecute_NoTokens(t *testing.T) {
// Bearer auth with no tokens should return an error from GetDeploymentAccessStatus
creds := &credentials.Credentials{
AuthType: "bearer",
}
logger := zerolog.New(io.Discard)
envSet := &environments.EnvironmentSet{}

rtCtx := &runtime.Context{
Credentials: creds,
Logger: &logger,
EnvironmentSet: envSet,
}

h := access.NewHandler(rtCtx)
err := h.Execute(context.Background())

if err == nil {
t.Fatal("expected error for missing tokens, got nil")
}
if !strings.Contains(err.Error(), "failed to check deployment access") {
t.Errorf("expected 'failed to check deployment access' error, got: %v", err)
}
}
6 changes: 4 additions & 2 deletions cmd/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package account
import (
"github.com/spf13/cobra"

"github.com/smartcontractkit/cre-cli/cmd/account/access"
"github.com/smartcontractkit/cre-cli/cmd/account/link_key"
"github.com/smartcontractkit/cre-cli/cmd/account/list_key"
"github.com/smartcontractkit/cre-cli/cmd/account/unlink_key"
Expand All @@ -12,10 +13,11 @@ import (
func New(runtimeContext *runtime.Context) *cobra.Command {
accountCmd := &cobra.Command{
Use: "account",
Short: "Manages account",
Long: "Manage your linked public key addresses for workflow operations.",
Short: "Manage account and request deploy access",
Long: "Manage your linked public key addresses for workflow operations and request deployment access.",
}

accountCmd.AddCommand(access.New(runtimeContext))
accountCmd.AddCommand(link_key.New(runtimeContext))
accountCmd.AddCommand(unlink_key.New(runtimeContext))
accountCmd.AddCommand(list_key.New(runtimeContext))
Expand Down
19 changes: 18 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/smartcontractkit/cre-cli/cmd/workflow"
"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/context"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/logger"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/settings"
Expand Down Expand Up @@ -193,7 +194,7 @@ func newRootCommand() *cobra.Command {

// Check if organization is ungated for commands that require it
cmdPath := cmd.CommandPath()
if cmdPath == "cre account link-key" || cmdPath == "cre workflow deploy" {
if cmdPath == "cre account link-key" {
if err := runtimeContext.Credentials.CheckIsUngatedOrganization(); err != nil {
if showSpinner {
spinner.Stop()
Expand Down Expand Up @@ -280,6 +281,21 @@ func newRootCommand() *cobra.Command {
cobra.AddTemplateFunc("styleURL", func(s string) string {
return ui.URLStyle.Render(s) // Chainlink Blue, underlined
})
cobra.AddTemplateFunc("needsDeployAccess", func() bool {
creds := runtimeContext.Credentials
if creds == nil {
var err error
creds, err = credentials.New(rootLogger)
if err != nil {
return false
}
}
deployAccess, err := creds.GetDeploymentAccessStatus()
if err != nil {
return false
}
return !deployAccess.HasAccess
})

rootCmd.SetHelpTemplate(helpTemplate)

Expand Down Expand Up @@ -368,6 +384,7 @@ func isLoadSettings(cmd *cobra.Command) bool {
"cre login": {},
"cre logout": {},
"cre whoami": {},
"cre account access": {},
"cre account list-key": {},
"cre init": {},
"cre generate-bindings": {},
Expand Down
6 changes: 6 additions & 0 deletions cmd/template/help_template.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
to login into your cre account, then:
{{styleCode "$ cre init"}}
to create your first cre project.
{{- if needsDeployAccess}}

🔑 Ready to deploy? Run:
$ cre account access
to request deployment access.
{{- end}}

{{styleSection "Need more help?"}}
Visit {{styleURL "https://docs.chain.link/cre"}}
Expand Down
13 changes: 13 additions & 0 deletions cmd/whoami/whoami.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func (h *Handler) Execute(ctx context.Context) error {
return fmt.Errorf("graphql request failed: %w", err)
}

// Get deployment access status
deployAccess, err := h.credentials.GetDeploymentAccessStatus()
if err != nil {
h.log.Debug().Err(err).Msg("failed to get deployment access status")
}

ui.Line()
ui.Title("Account Details")

Expand All @@ -101,6 +107,13 @@ func (h *Handler) Execute(ctx context.Context) error {
details)
}

// Add deployment access status
if deployAccess != nil && deployAccess.HasAccess {
details = fmt.Sprintf("%s\nDeploy Access: Enabled", details)
} else {
details = fmt.Sprintf("%s\nDeploy Access: Not enabled", details)
}

ui.Box(details)
ui.Line()

Expand Down
14 changes: 13 additions & 1 deletion cmd/workflow/deploy/compile_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deploy

import (
"context"
"encoding/base64"
"errors"
"io"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/settings"
"github.com/smartcontractkit/cre-cli/internal/testutil/chainsim"
"github.com/smartcontractkit/cre-cli/internal/validation"
Expand Down Expand Up @@ -92,6 +94,11 @@ func TestCompileCmd(t *testing.T) {
defer simulatedEnvironment.Close()

ctx, buf := simulatedEnvironment.NewRuntimeContextWithBufferedOutput()
ctx.Credentials = &credentials.Credentials{
APIKey: "test-api-key",
AuthType: credentials.AuthTypeApiKey,
IsValidated: true,
}
handler := newHandler(ctx, buf)

ctx.Settings = createTestSettings(
Expand Down Expand Up @@ -198,6 +205,11 @@ func TestCompileCmd(t *testing.T) {
os.Stdout = w

ctx, buf := simulatedEnvironment.NewRuntimeContextWithBufferedOutput()
ctx.Credentials = &credentials.Credentials{
APIKey: "test-api-key",
AuthType: credentials.AuthTypeApiKey,
IsValidated: true,
}
handler := newHandler(ctx, buf)

ctx.Settings = createTestSettings(
Expand All @@ -212,7 +224,7 @@ func TestCompileCmd(t *testing.T) {
err := handler.ValidateInputs()
require.NoError(t, err)

err = handler.Execute()
err = handler.Execute(context.Background())

w.Close()
os.Stdout = oldStdout
Expand Down
26 changes: 22 additions & 4 deletions cmd/workflow/deploy/deploy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deploy

import (
"context"
"errors"
"fmt"
"io"
Expand All @@ -12,6 +13,7 @@ import (
"github.com/spf13/viper"

"github.com/smartcontractkit/cre-cli/cmd/client"
"github.com/smartcontractkit/cre-cli/internal/accessrequest"
"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
Expand Down Expand Up @@ -61,6 +63,7 @@ type handler struct {
workflowArtifact *workflowArtifact
wrc *client.WorkflowRegistryV2Client
runtimeContext *runtime.Context
accessRequester *accessrequest.Requester

validated bool

Expand Down Expand Up @@ -93,7 +96,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
if err := h.ValidateInputs(); err != nil {
return err
}
return h.Execute()
return h.Execute(cmd.Context())
},
}

Expand All @@ -117,10 +120,16 @@ func newHandler(ctx *runtime.Context, stdin io.Reader) *handler {
workflowArtifact: &workflowArtifact{},
wrc: nil,
runtimeContext: ctx,
accessRequester: accessrequest.NewRequester(ctx.Credentials, ctx.EnvironmentSet, ctx.Logger),
validated: false,
wg: sync.WaitGroup{},
wrcErr: nil,
}

return &h
}

func (h *handler) initWorkflowRegistryClient() {
h.wg.Add(1)
go func() {
defer h.wg.Done()
Expand All @@ -131,8 +140,6 @@ func newHandler(ctx *runtime.Context, stdin io.Reader) *handler {
}
h.wrc = wrc
}()

return &h
}

func (h *handler) ResolveInputs(v *viper.Viper) (Inputs, error) {
Expand Down Expand Up @@ -181,7 +188,18 @@ func (h *handler) ValidateInputs() error {
return nil
}

func (h *handler) Execute() error {
func (h *handler) Execute(ctx context.Context) error {
deployAccess, err := h.credentials.GetDeploymentAccessStatus()
if err != nil {
return fmt.Errorf("failed to check deployment access: %w", err)
}

if !deployAccess.HasAccess {
return h.accessRequester.PromptAndSubmitRequest(ctx)
}

h.initWorkflowRegistryClient()

h.displayWorkflowDetails()

if err := h.Compile(); err != nil {
Expand Down
Loading
Loading