From 82cb4616cfb8ca7e20885893460779e1dc65ab98 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Wed, 5 Aug 2026 15:04:48 -0400 Subject: [PATCH 1/2] feat: conclude set-icon experiment and enable icon upload for all apps --- docs/reference/experiments.md | 2 +- internal/experiment/experiment.go | 4 --- internal/pkg/apps/install.go | 37 ++++++-------------- internal/pkg/apps/install_test.go | 58 ++----------------------------- 4 files changed, 15 insertions(+), 86 deletions(-) diff --git a/docs/reference/experiments.md b/docs/reference/experiments.md index 13bf0902..9a14b35a 100644 --- a/docs/reference/experiments.md +++ b/docs/reference/experiments.md @@ -8,12 +8,12 @@ The following is a list of currently available experiments. We'll remove experim - `lipgloss`: shows pretty styles. - `manifest-sync`: resolves conflicting app manifest values. -- `set-icon`: enables icon upload for non-hosted apps ([PR#469](https://github.com/slackapi/slack-cli/pull/469)). ## Experiments changelog Below is a list of updates related to experiments. +- **August 2026**: Concluded the `set-icon` experiment with full support for icon upload on all app types now enabled by default in the Slack CLI. - **July 2026**: Added the `manifest-sync` experiment to resolve changed app manifest values between a project and app settings. - **April 2026**: Concluded the `sandboxes` experiment with full support in the Slack CLI. Refer to the [`slack sandbox create`](/tools/slack-cli/reference/commands/slack_sandbox_create/), [`slack sandbox delete`](/tools/slack-cli/reference/commands/slack_sandbox_delete/), and [`slack sandbox list`](/tools/slack-cli/reference/commands/slack_sandbox_list/) commands for more details. - **April 2026**: Added the `set-icon` experiment to enable icon upload for non-hosted apps. diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go index cec00da1..c34894c1 100644 --- a/internal/experiment/experiment.go +++ b/internal/experiment/experiment.go @@ -38,9 +38,6 @@ const ( // Placeholder experiment is a placeholder for testing and does nothing... or does it? Placeholder Experiment = "placeholder" - - // SetIcon experiment enables icon upload for non-hosted apps. - SetIcon Experiment = "set-icon" ) // AllExperiments is a list of all available experiments that can be enabled @@ -49,7 +46,6 @@ var AllExperiments = []Experiment{ Lipgloss, ManifestSync, Placeholder, - SetIcon, } // EnabledExperiments is a list of experiments that are permanently enabled diff --git a/internal/pkg/apps/install.go b/internal/pkg/apps/install.go index 93f61285..5c6a1953 100644 --- a/internal/pkg/apps/install.go +++ b/internal/pkg/apps/install.go @@ -220,7 +220,7 @@ func Install(ctx context.Context, clients *shared.ClientFactory, auth types.Slac iconPath := resolveIconPath(ctx, clients, slackManifest.Icon) if iconPath != "" { - err = updateIcon(ctx, clients, iconPath, app.AppID, token, manifest.IsFunctionRuntimeSlackHosted()) + err = updateIcon(ctx, clients, iconPath, app.AppID, token) if err != nil { clients.IO.PrintDebug(ctx, "icon error: %s", err) _, _ = clients.IO.WriteOut().Write([]byte(style.SectionSecondaryf("Error updating app icon: %s", err))) @@ -516,17 +516,14 @@ func InstallLocalApp(ctx context.Context, clients *shared.ClientFactory, orgGran return app, result, installState, err } - // upload icon for non-hosted apps (gated behind set-icon experiment) - if clients.Config.WithExperimentOn(experiment.SetIcon) { - iconPath := resolveIconPath(ctx, clients, slackManifest.Icon) - if iconPath != "" { - _, iconErr := clients.API().IconSet(ctx, clients.Fs, token, app.AppID, iconPath) - if iconErr != nil { - clients.IO.PrintDebug(ctx, "icon error: %s", iconErr) - _, _ = clients.IO.WriteOut().Write([]byte(style.SectionSecondaryf("Error updating app icon: %s", iconErr))) - } else { - _, _ = clients.IO.WriteOut().Write([]byte(style.SectionSecondaryf("Updated app icon: %s", iconPath))) - } + iconPath := resolveIconPath(ctx, clients, slackManifest.Icon) + if iconPath != "" { + _, iconErr := clients.API().IconSet(ctx, clients.Fs, token, app.AppID, iconPath) + if iconErr != nil { + clients.IO.PrintDebug(ctx, "icon error: %s", iconErr) + _, _ = clients.IO.WriteOut().Write([]byte(style.SectionSecondaryf("Error updating app icon: %s", iconErr))) + } else { + _, _ = clients.IO.WriteOut().Write([]byte(style.SectionSecondaryf("Updated app icon: %s", iconPath))) } } @@ -661,28 +658,16 @@ func resolveIconPath(ctx context.Context, clients *shared.ClientFactory, manifes } // updateIcon will upload the new icon to the Slack API -func updateIcon(ctx context.Context, clients *shared.ClientFactory, iconPath, appID string, token string, isHosted bool) error { +func updateIcon(ctx context.Context, clients *shared.ClientFactory, iconPath, appID string, token string) error { var span opentracing.Span span, ctx = opentracing.StartSpanFromContext(ctx, "updateIcon") defer span.Finish() - var err error - if clients.Config.WithExperimentOn(experiment.SetIcon) { - _, err = clients.API().IconSet(ctx, clients.Fs, token, appID, iconPath) - } else if isHosted { - // DEPRECATED: Prefer IconSet once the SetIcon experiment concludes - _, err = clients.API().Icon(ctx, clients.Fs, token, appID, iconPath) - } else { - return nil - } + _, err := clients.API().IconSet(ctx, clients.Fs, token, appID, iconPath) if err != nil { - // TODO: separate the icon upload into a different function because if an error is returned - // the new app_id might be ignored and next time we'll create another app. return fmt.Errorf("%s %s", err, iconPath) } - // Save a md5 hash of the icon in environments.yaml - // env.IconHash = iconResp.MD5Hash return nil } diff --git a/internal/pkg/apps/install_test.go b/internal/pkg/apps/install_test.go index 52a04953..3f782cc4 100644 --- a/internal/pkg/apps/install_test.go +++ b/internal/pkg/apps/install_test.go @@ -16,7 +16,6 @@ package apps import ( "bytes" - "context" "fmt" "testing" @@ -24,7 +23,6 @@ import ( "github.com/slackapi/slack-cli/internal/app" "github.com/slackapi/slack-cli/internal/cache" "github.com/slackapi/slack-cli/internal/config" - "github.com/slackapi/slack-cli/internal/experiment" "github.com/slackapi/slack-cli/internal/shared" "github.com/slackapi/slack-cli/internal/shared/types" "github.com/slackapi/slack-cli/internal/slackcontext" @@ -1811,45 +1809,11 @@ func Test_resolveIconPath(t *testing.T) { func Test_updateIcon(t *testing.T) { tests := map[string]struct { - isHosted bool - experimentOn bool - expectIconSet bool - expectIcon bool - expectSkip bool mockError error expectedError bool }{ - "experiment on + hosted app uses IconSet": { - isHosted: true, - experimentOn: true, - expectIconSet: true, - }, - "experiment on + non-hosted app uses IconSet": { - isHosted: false, - experimentOn: true, - expectIconSet: true, - }, - "experiment off + hosted app uses Icon": { - isHosted: true, - experimentOn: false, - expectIcon: true, - }, - "experiment off + non-hosted app skips upload": { - isHosted: false, - experimentOn: false, - expectSkip: true, - }, + "succeeds with IconSet": {}, "returns error from IconSet": { - isHosted: false, - experimentOn: true, - expectIconSet: true, - mockError: fmt.Errorf("api error"), - expectedError: true, - }, - "returns error from Icon": { - isHosted: true, - experimentOn: false, - expectIcon: true, mockError: fmt.Errorf("api error"), expectedError: true, }, @@ -1860,18 +1824,11 @@ func Test_updateIcon(t *testing.T) { clientsMock := shared.NewClientsMock() clientsMock.AddDefaultMocks() - if tc.experimentOn { - clientsMock.Config.ExperimentsFlag = []string{string(experiment.SetIcon)} - clientsMock.Config.LoadExperiments(ctx, func(_ context.Context, _ string, _ ...interface{}) {}) - } - clientsMock.API.On("IconSet", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(api.IconResult{}, tc.mockError) - clientsMock.API.On("Icon", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(api.IconResult{}, tc.mockError) clients := shared.NewClientFactory(clientsMock.MockClientFactory()) - err := updateIcon(ctx, clients, "icon.png", "A001", "xoxe-token", tc.isHosted) + err := updateIcon(ctx, clients, "icon.png", "A001", "xoxe-token") if tc.expectedError { require.Error(t, err) @@ -1879,16 +1836,7 @@ func Test_updateIcon(t *testing.T) { require.NoError(t, err) } - if tc.expectIconSet { - clientsMock.API.AssertCalled(t, "IconSet", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) - clientsMock.API.AssertNotCalled(t, "Icon") - } else if tc.expectIcon { - clientsMock.API.AssertCalled(t, "Icon", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) - clientsMock.API.AssertNotCalled(t, "IconSet") - } else if tc.expectSkip { - clientsMock.API.AssertNotCalled(t, "Icon") - clientsMock.API.AssertNotCalled(t, "IconSet") - } + clientsMock.API.AssertCalled(t, "IconSet", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) }) } } From 43a1f6e35ed7d9ce40a6f7573a3f0d9dc867b001 Mon Sep 17 00:00:00 2001 From: Ale Mercado <104795114+srtaalej@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:48:30 -0400 Subject: [PATCH 2/2] Update docs/reference/experiments.md Co-authored-by: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> --- docs/reference/experiments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/experiments.md b/docs/reference/experiments.md index 9a14b35a..a5989207 100644 --- a/docs/reference/experiments.md +++ b/docs/reference/experiments.md @@ -13,7 +13,7 @@ The following is a list of currently available experiments. We'll remove experim Below is a list of updates related to experiments. -- **August 2026**: Concluded the `set-icon` experiment with full support for icon upload on all app types now enabled by default in the Slack CLI. +- **August 2026**: Concluded the `set-icon` experiment; The Slack CLI now offers full support for icon upload on all app types by default. - **July 2026**: Added the `manifest-sync` experiment to resolve changed app manifest values between a project and app settings. - **April 2026**: Concluded the `sandboxes` experiment with full support in the Slack CLI. Refer to the [`slack sandbox create`](/tools/slack-cli/reference/commands/slack_sandbox_create/), [`slack sandbox delete`](/tools/slack-cli/reference/commands/slack_sandbox_delete/), and [`slack sandbox list`](/tools/slack-cli/reference/commands/slack_sandbox_list/) commands for more details. - **April 2026**: Added the `set-icon` experiment to enable icon upload for non-hosted apps.