Conversation
Codecov Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
mwbrooks
left a comment
There was a problem hiding this comment.
🙇🏻 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! 🎉
| 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 { |
There was a problem hiding this comment.
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.
| } 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 { |
There was a problem hiding this comment.
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.
| 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)) | ||
| } |
There was a problem hiding this comment.
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)| })) | ||
|
|
||
| // Fetch remote manifest and write it to the local project | ||
| syncRemedy := " Run %s to sync manually" |
There was a problem hiding this comment.
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": { |
There was a problem hiding this comment.
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.
…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.
Changelog
slack create --appnow 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 byslack manifest sync. This PR consolidates them — when--appis passed toslack create, the remote manifest is fetched from app settings and written to the local project after linking.Changes:
LinkExistingAppnow returns the auth token so callers can use it for follow-up API callsGetManifestRemoteand written locally viaWriteManifestLocalslack manifest sync --manifest-source=remotemanuallyPreview
No UI changes — output is the same as before, minus the need for a separate
slack manifest syncstep.Testing
make buildcdinto the created project and run./bin/slack runNotes
slack manifest sync --manifest-source=remotemanuallymanifest.Syncis unchanged — the create flow inlines only the fetch and write steps since it always uses remote as the source of truthRequirements