feat: add slack manifest diff command#591
Draft
mwbrooks wants to merge 11 commits into
Draft
Conversation
Compares the project manifest against app settings and prints any differences. Read-only — no API mutations, no file writes, no prompts. Useful for CI guardrails, pre-flight visibility before deploy, and debugging manifest drift. Carved out from the larger two-way manifest sync work (#543) so the diff capability can land independently with a smaller diff.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #591 +/- ##
==========================================
- Coverage 71.67% 71.63% -0.04%
==========================================
Files 226 230 +4
Lines 19176 19391 +215
==========================================
+ Hits 13744 13891 +147
- Misses 4221 4275 +54
- Partials 1211 1225 +14 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Shows the non-interactive form using --app and --token, matching the intended CI guardrail use case.
Replaces "Detect manifest drift in CI" with "Show manifest differences without prompts" so the language is plain and accessible to developers who haven't encountered drift terminology before.
Documents flattenRecursive, diffFlat, valuesEqual, and formatValue to match the project's existing convention (e.g. cmd/manifest/info.go documents both exported and unexported helpers).
DisplayDiffs printed a leading blank inside the loop, which combined with the trailing newline from style.Sectionf to produce a double blank between the header and the first difference. Skip the blank on the first iteration so spacing only appears between entries.
apps.manifest.export does not echo _metadata back, so projects that declared it (e.g. SDK schema annotations) saw a noisy "(only in project)" entry on every diff run. Apply a small ignored-paths filter inside Diff so these never reach the user, and leave the list extensible for any future one-sided fields.
formatValue used a byte-based slice (s[:77]) for the truncation, which could cut a multi-byte UTF-8 character in half and produce an invalid string in the middle of a localized field value. Switch to a rune-based truncate helper and cover it with a test that includes multi-byte input.
The Test_Diff and Test_Flatten loops previously only verified that expected entries were present, so a regression that produced extra unrelated entries would not have failed. Add a Len assertion before the per-entry checks. Also update the function-related cases to account for ManifestFunction.InputParameters and OutputParameters (which lack the omitempty tag and therefore always serialize as null).
Slack's apps.manifest.export appends " (local)" to display_information.name and features.bot_user.display_name when returning the manifest of a dev-installed app. Fresh installs would otherwise show two phantom diffs immediately. Suppress these specifically when removing the suffix would make the values equal, so genuine renames still surface.
apps.manifest.export emits settings.is_mcp_enabled: false for every app even when the project never declared the field, so users would otherwise see a phantom "(only in app settings)" entry on every run. Suppress it specifically when the value is false; if a user sets it to true locally, the resulting Modified diff still surfaces.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changelog
Summary
This pull request adds a read-only
slack manifest diffcommand that compares the project manifest against the app settings on Slack and prints any differences.slack manifest synccommand will build on these primitives.slack manifest validate(which exits 0 even when the manifest is invalid). Standardizing exit-code semantics for scripting use-cases is a separate, project-wide conversation that should changevalidateanddifftogether.Preview
Testing
Outside of a project will error:
Create a project:
Inside of a project with no App IDs will error:
Create an App ID:
Compare Project to App Settings Manifest:
$ ../bin/slack manifest diff 📚 App Manifest Project manifest and app settings are in syncslack manifest diffinside a project where the local manifest matches app settings; verify the "Project manifest and app settings are in sync" message appears.display_information.name) without deploying. Runslack manifest diffand verify the changed field is listed with both the project value and the app settings value.slack manifest diffand verify it shows up under "(only in project)".slack manifest --helpand verifydiffappears alongsideinfoandvalidate.slack manifest diff --app <app-id> --token <token>(or withSLACK_AUTH_TOKENset) and verify the command runs end-to-end with no interactive prompts — the same diff output is produced as the interactive case.Notes
stylepackage formatting carried over from the sync branch. If reviewers prefer a leaner format (e.g., one line per diff), happy to simplify in a follow-up.Requirements