Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
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
72eceef
Merge branch 'feature/charm-lib-refactoring' into feature/DEVSVCS-372…
ejacquier Feb 4, 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
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 @@ -187,7 +188,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 @@ -274,6 +275,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 @@ -362,6 +378,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
15 changes: 15 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,15 @@ func (h *Handler) Execute(ctx context.Context) error {
details)
}

// Add deployment access status
if deployAccess != nil {
if 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
3 changes: 2 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 Down Expand Up @@ -212,7 +213,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 @@ -13,6 +14,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 @@ -62,6 +64,7 @@ type handler struct {
workflowArtifact *workflowArtifact
wrc *client.WorkflowRegistryV2Client
runtimeContext *runtime.Context
accessRequester *accessrequest.Requester

validated bool

Expand Down Expand Up @@ -94,7 +97,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 @@ -118,10 +121,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 @@ -132,8 +141,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 @@ -177,7 +184,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