Skip to content

feat(cmd): auto-sync manifest from remote when creating with --app - #677

Open
srtaalej wants to merge 10 commits into
mainfrom
ale-consolidate-create-sync
Open

srtaalej wants to merge 10 commits into
mainfrom
ale-consolidate-create-sync

Conversation

@srtaalej

@srtaalej srtaalej commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Changelog

slack create --app now automatically syncs the manifest from app settings to the local project.

Summary

The CLI handoff from app settings to local development currently requires two commands: slack create --app ... --template ... followed by slack manifest sync. This PR consolidates them — when --app is passed to slack create, the remote manifest is fetched from app settings and written to the local project after linking.

Changes:

  • LinkExistingApp now returns the auth token so callers can use it for follow-up API calls
  • After linking an app during create, the remote manifest is fetched via GetManifestRemote and written locally via WriteManifestLocal
  • If the fetch or write fails, a warning is shown with instructions to run slack manifest sync --manifest-source=remote manually

Preview

No UI changes — output is the same as before, minus the need for a separate slack manifest sync step.

Testing

  1. Go to https://api.slack.com/apps and create a new app through the "Create New App" flow
  2. Note the App ID and Team ID from the app settings page
  3. Build the CLI: make build
  4. Run:
    ./bin/slack create --template slack-samples/bolt-js-starter-template --app <APP_ID> --name "test-create-sync" --team <TEAM_ID> --environment local
    
  5. cd into the created project and run ./bin/slack run
  6. Verify there is no manifest sync prompt — the project manifest should already match app settings

Notes

  • The manifest fetch/write error is non-blocking — if it fails, the project is still usable and the user can run slack manifest sync --manifest-source=remote manually
  • manifest.Sync is unchanged — the create flow inlines only the fetch and write steps since it always uses remote as the source of truth

Requirements

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.23%. Comparing base (20dd730) to head (e3eeae4).

Files with missing lines Patch % Lines
cmd/project/create.go 60.00% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #677   +/-   ##
=======================================
  Coverage   78.23%   78.23%           
=======================================
  Files         239      239           
  Lines       18149    18159   +10     
=======================================
+ Hits        14198    14206    +8     
- Misses       3951     3953    +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@srtaalej srtaalej self-assigned this Sep 24, 2026
@srtaalej srtaalej added enhancement M-T: A feature request for new functionality semver:minor Use on pull requests to describe the release version increment labels Sep 24, 2026
@srtaalej srtaalej added this to the Next Release milestone Sep 24, 2026
@srtaalej
srtaalej marked this pull request as ready for review September 24, 2026 17:45
@srtaalej
srtaalej requested a review from a team as a code owner September 24, 2026 17:45
@srtaalej srtaalej changed the title feat: auto-sync manifest from remote when creating with --app feat(cmd): auto-sync manifest from remote when creating with --app Sep 24, 2026
@srtaalej
srtaalej requested review from mwbrooks and zimeg September 24, 2026 18:49

@mwbrooks mwbrooks left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙇🏻 Thanks @srtaalej, this is a really nice improvement! Folding manifest sync into slack create --app removes a whole step from the app settings to local development handoff, and keeping the fetch/write non-blocking is the right call. 🙌

I left a few inline comments. The two blocking ones are about what ends up in manifest.json: the (local) name suffix for local apps, and silently skipping the write when there's no manifest.json (e.g. Deno projects). The others are smaller suggestions to make the handoff fully seamless and to cover the success path in tests.

📝 One small thing on the PR description: it mentions "LinkExistingApp now returns the auth token", but the diff resolves auth with a separate AuthWithTeamID call instead. Could you update the description to match?

Looking forward to seeing this one land! 🎉

Comment thread cmd/project/create.go
if auth, err := clients.Auth().AuthWithTeamID(ctx, linkedApp.TeamID); err != nil {
clients.IO.PrintWarning(ctx, "Failed to resolve auth for manifest sync: %s", err)
clients.IO.PrintInfo(ctx, false, syncRemedy, style.Commandf("manifest sync --manifest-source=remote", false))
} else if remoteManifest, err := clients.AppClient().Manifest.GetManifestRemote(ctx, auth.Token, linkedApp.AppID); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): For --environment local apps, apps.manifest.export appends (local) to display_information.name and features.bot_user.display_name. Writing remoteManifest.AppManifest as-is stores that suffix into the project's manifest.json, so the app becomes "My App (local)" locally, and a later slack deploy would ship that name to the deployed app.

manifest.Sync avoids this by ignoring the suffix diff (isDevLocalSuffixDiff in internal/manifest/diff.go). Could we strip the (local) suffix from those paths before writing when linkedApp.IsDev is true? A small helper in internal/manifest next to devLocalSuffixPaths would keep both paths consistent.

Comment thread cmd/project/create.go
} else if remoteManifest, err := clients.AppClient().Manifest.GetManifestRemote(ctx, auth.Token, linkedApp.AppID); err != nil {
clients.IO.PrintWarning(ctx, "Failed to fetch manifest from app settings: %s", err)
clients.IO.PrintInfo(ctx, false, syncRemedy, style.Commandf("manifest sync --manifest-source=remote", false))
} else if _, err := manifest.WriteManifestLocal(clients.Fs, absProjectPath, remoteManifest.AppManifest); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): The WriteBackResult is discarded here, but WriteManifestLocal returns Written: false with a Warning (and a nil error) when there's no manifest.json in the project root. That's the case for Deno templates (manifest.ts) and any template that keeps its manifest elsewhere. In that case the user sees no output and assumes the sync worked.

Could we mirror what Sync does at internal/manifest/sync.go:135-139? Print ✓ Updated manifest.json when Written is true, or surface the Warning otherwise.

Comment thread cmd/project/create.go
Comment on lines +245 to +254
if auth, err := clients.Auth().AuthWithTeamID(ctx, linkedApp.TeamID); err != nil {
clients.IO.PrintWarning(ctx, "Failed to resolve auth for manifest sync: %s", err)
clients.IO.PrintInfo(ctx, false, syncRemedy, style.Commandf("manifest sync --manifest-source=remote", false))
} else if remoteManifest, err := clients.AppClient().Manifest.GetManifestRemote(ctx, auth.Token, linkedApp.AppID); err != nil {
clients.IO.PrintWarning(ctx, "Failed to fetch manifest from app settings: %s", err)
clients.IO.PrintInfo(ctx, false, syncRemedy, style.Commandf("manifest sync --manifest-source=remote", false))
} else if _, err := manifest.WriteManifestLocal(clients.Fs, absProjectPath, remoteManifest.AppManifest); err != nil {
clients.IO.PrintWarning(ctx, "Failed to write manifest to project: %s", err)
clients.IO.PrintInfo(ctx, false, syncRemedy, style.Commandf("manifest sync --manifest-source=remote", false))
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Sync also refreshes the cached manifest hash after writing (internal/manifest/sync.go:121-127), which this block skips. Without a saved hash, the first slack run goes through shouldUpdateManifest (internal/pkg/apps/install.go:735-742), exports the manifest again, and runs the full Sync. It won't prompt, since local and remote now match, but it's an extra round trip and prints the "in sync" section on every run.

Setting the hash from the remote manifest after a successful write would make the handoff actually seamless:

hash, err := clients.Config.ProjectConfig.Cache().NewManifestHash(ctx, remoteManifest.AppManifest)
// ...
err = clients.Config.ProjectConfig.Cache().SetManifestHash(ctx, linkedApp.AppID, hash)

Comment thread cmd/project/create.go
}))

// Fetch remote manifest and write it to the local project
syncRemedy := " Run %s to sync manually"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: For projects whose manifest source is remote, the suggested manifest sync --manifest-source=remote command errors with "Manifest sync is unavailable for projects with remote source of truth" (internal/manifest/sync.go:43-50).

Should we check ProjectConfig.GetManifestSource first? For remote-source projects the local manifest.json isn't used, so would could skip this suggestion.

assert.True(t, saved.IsDev)
},
},
"app flag with manifest fetch error shows warning but succeeds": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This case only asserts the command succeeds; it doesn't check that the warning or the remedy was printed. It would be nice to assert on the output (e.g. cm.IO.AssertCalled(t, "PrintWarning", ...) or checking the stdout buffer).

The success path also isn't really covered. absProjectPath is a t.TempDir() path with no manifest.json in the mocked Fs, so every test hits the "no manifest.json" branch of WriteManifestLocal. Could we add a case that seeds a manifest.json into cm.Fs at the project path, returns a non-empty remote manifest from GetManifestRemote, and asserts on the written file? Using a (local)-suffixed name with --environment local there would also cover the suffix stripping.

@mwbrooks mwbrooks modified the milestones: v4.9.0, Next Release Oct 2, 2026
mwbrooks added a commit that referenced this pull request Oct 2, 2026
…684)

* ci: keep the release milestone open until open items are moved

The issues endpoint can miss items right after the milestone is renamed,
which left #677 on the closed v4.9.0 milestone. The open count of the
milestone is now checked and the move is retried before closing it.

* ci: list open milestone items before the rename instead of retrying

Milestone titles are unique, so the next milestone cannot be created
before the rename. Listing the open items first avoids the rename
entirely and replaces the retry loop.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality semver:minor Use on pull requests to describe the release version increment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants