fix: replace manifest.txt help mechanism with embedded user docs directory#6919
fix: replace manifest.txt help mechanism with embedded user docs directory#6919robertolopezlopez wants to merge 1 commit into
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
9714e0c to
bd242f1
Compare
This comment has been minimized.
This comment has been minimized.
|
/describe |
|
PR Description updated to latest commit (bd242f1) |
this is just mimicking the previous .ts behavior
this failure would just happen in build time, not runtime |
bd242f1 to
4d63ea3
Compare
This comment has been minimized.
This comment has been minimized.
| @@ -0,0 +1 @@ | |||
| Adding this file to the project will prevent IDE errors between builds No newline at end of file | |||
There was a problem hiding this comment.
Question: does this directory need to go into gitignore
There was a problem hiding this comment.
mmmmh good one, adding - but you get the reasoning behind this file, right?
There was a problem hiding this comment.
Could we perhaps ignore the directory contents entirely in case an interrupted make build/test leaves a dirty tree such that they cannot be committed by accident?
There was a problem hiding this comment.
This is what I meant, gitignore the whole directory and just have this one file.
but you get the reasoning behind this file, right?
I think this file is here so that a build would work even without usingmake buildfor it?!
There was a problem hiding this comment.
ok! for sure
4d63ea3 to
2cc8e09
Compare
This comment has been minimized.
This comment has been minimized.
| const cliCommandsDir = "cli-commands" | ||
|
|
||
| //go:embed all:cli-commands | ||
| var cliCommands embed.FS |
There was a problem hiding this comment.
Could we perhaps make the top level /help a go module itself, so we don't need this copy?
There was a problem hiding this comment.
Unfortunately not and this is why we bring up this Makefile wizardry. Root reason: it is off the cliv2/go.mod file tree. This is enforced by the directive itself: Patterns may not contain ‘.’ or ‘..’ or empty path elements
There was a problem hiding this comment.
and we cannot move help/ without extra trouble because there is a git hook which pushes directly to help/cli-commands/
| .PHONY: lint | ||
| lint: $(TOOLS_BIN)/golangci-lint | ||
| golangci-lint run -v | ||
| @$(MAKE) _helpdocs-prepare |
There was a problem hiding this comment.
what's the purpose of _helpdocs-prepare in lint?
There was a problem hiding this comment.
Leftover from previous approach - cleaning up
| var testCLICommandDocFiles map[string]struct{} | ||
|
|
||
| func TestMain(m *testing.M) { | ||
| teardown, err := setupCLICommandsForTest() |
There was a problem hiding this comment.
Could we test the logic against an in-memory fstest.MapFS to avoid the copy-delete process?
There was a problem hiding this comment.
I would prefer to mirror tHe Makefile#_Helpdocs-prepare. fstest.MapFS would only test against a made up FS tree, not the current state of help/cli-commands/*.md. I might add some extra test with fstest.MapFS - but probably this is redundant.
Please share your thoughts :-)
There was a problem hiding this comment.
I ended up applying this
2cc8e09 to
1ea64c4
Compare
This comment has been minimized.
This comment has been minimized.
1ea64c4 to
c13ac12
Compare
| var manifest string | ||
| const cliCommandsDir = "cli-commands" | ||
|
|
||
| //go:embed all:cli-commands |
There was a problem hiding this comment.
Suggestion: all seems unnecessary and risky to include unwanted filesystem content
| exit 1; \ | ||
| fi; \ | ||
| ls $(HELPDOCS_SOURCE) | xargs -n1 basename | sort > $(HELPDOCS_MANIFEST) | ||
| rm -rf $(HELPDOCS_EMBED_DIR)/*.md; \ |
There was a problem hiding this comment.
Suggestion: to be sure we start from an empty dir, let's just remove all file please.
This comment has been minimized.
This comment has been minimized.
c13ac12 to
27af388
Compare
This comment has been minimized.
This comment has been minimized.
27af388 to
81366f3
Compare
This comment has been minimized.
This comment has been minimized.
| cp $(HELPDOCS_SOURCE)/*.md $(HELPDOCS_EMBED_DIR)/ | ||
|
|
||
| $(BUILD_DIR)/$(V2_EXECUTABLE_NAME): $(BUILD_DIR) $(SRCS) generate-ls-protocol-metadata $(HELPDOCS_MANIFEST) | ||
| _helpdocs-clean: |
There was a problem hiding this comment.
Question: Now that we have _helpdocs-prepare and _helpdocs-clean. Could it be possible just to generate the manifest.txt with the filenames, embed that, and then remove it?
Will the contents of .md files end up being embedded twice in the binary as //go:embed cli-commands doesn't include strictly just the file names, and I believe they're also embedded by the legacy TS version?
There was a problem hiding this comment.
We are not generating the manifest.txt due to the user-docs hook issue; that's the whole purpose of this PR.
I do not follow your second comment :-)
There was a problem hiding this comment.
On the manifest point, I think we're aligned that a committed manifest.txt is the problem: because of the weekly sync-cli-help-to-user-docs action, the manifest could drift.
What I was wondering is whether a transient manifest (generated in _helpdocs-prepare, embedded for compile, removed in _helpdocs-clean, never committed) would still have that problem. It feels like it would solve the same drift issue, just with a smaller Go-side embed than copying all the .md files. Does that match your thinking, or am I missing something about why even a build-time-only manifest is undesirable?
On the second part, Go's //go:embed cli-commands pulls in the full markdown files (with content), but HasUserDoc only uses their filenames for routing.
Mostly curious whether I'm reasoning about the transient-manifest alternative correctly, or if there's a constraint I'm not seeing. Happy to resolve the thread either way once I've understood your take. 🙂
There was a problem hiding this comment.
But what's the point of generating a manifest.txt in _helpdocs-prepare. I think that's adding extra complexity:
- Iterate over all user-docs
- Create
manifest.txt - At the end, removing the manifest
Vs what we have here. embed.FS is basically an iterable construct which does not imply any complexity other than iterating it.
I do not think it brings any advantage. If you could name any real, tangible advantage we may be in the same line ;-)
There was a problem hiding this comment.
With this approach we're embedding the docs content twice in the shipped binary: Go embeds the full .md files but only uses filenames for routing, and the legacy TS binary already embeds the same content for rendering. With a transient manifest we'd only have the filenames on the Go side. The difference isn't big though 😅 .
There was a problem hiding this comment.
You may be right on one side: yes, double embed. The TS bundle will be there for long time I believe.
Your proposal may work and shrink the Go side; but reopens the initial approach in CLI-1596 and adds more problems to the PR, which is already large.
Planned scope: preparing+cleaning changes, embed, getting a green pipeline (heh), multiple new review threads.
Do you really think it is worth to start changing the whole PR? :)
There was a problem hiding this comment.
Fair point. Thank you 😀
Approval already given. I believe you can merge this PR.
There was a problem hiding this comment.
thank youuuu
There was a problem hiding this comment.
just to add a closing thought here, we will be removing the TS implementation in the future, so the double embed is only temporary.
| openboxtest: | ||
| @echo "$(LOG_PREFIX) Running $@" | ||
| @$(GOCMD) test -cover ./... | ||
| @$(MAKE) _helpdocs-prepare |
There was a problem hiding this comment.
Question: can we test against synthetic data rather then relying on this pre-test step
There was a problem hiding this comment.
_helpdocs-prepare needs to be run before go test, so the test binary embeds the real .md (compile time vs run time). So we test with real world data :-)
Does this address your question, or what do you really mean with synthetic data?
There was a problem hiding this comment.
I think I meant what @octavian-snyk meant with their comment.
81366f3 to
f62f6c8
Compare
This comment has been minimized.
This comment has been minimized.
|
|
||
| var testCLICommandDocFiles map[string]struct{} | ||
|
|
||
| func TestMain(m *testing.M) { |
There was a problem hiding this comment.
Question: do we really/still need this?
f62f6c8 to
2df896c
Compare
This comment has been minimized.
This comment has been minimized.
2df896c to
1bd88c1
Compare
This comment has been minimized.
This comment has been minimized.
1bd88c1 to
e0247a9
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
nitpick: For readability it might be better to integrate these small files (embed.go, and export_test.go) into other relevant files; perhaps command_help.go, and fixture.go, respectively.
Other than this, LGTM :)
There was a problem hiding this comment.
ack :-) about embed.go: the Go convention is that //go:embed sits in a minimal file. I may merge it with command_help.go if you prefer but it is getting dangerously large IMHO and this is a sleepery road ;-)
There was a problem hiding this comment.
in snyk you can find similar examples https://github.com/snyk/policy-engine/blob/main/rego/embed.go
There was a problem hiding this comment.
about export_test.go: export_help.go is a kind of standard for test only package helpers. Example: os/export_test.go, fmt/export_test.go, etc.
There was a problem hiding this comment.
TIL, thanks for the explanation!
I noticed CommandHelpFromRepo is already used directly in a couple of test files (helprouting_test.go:233 and externally at main_test.go:770), while CommandHelpForTest is only used internally at command_help_test.go:17. Do we need the extra re-export via CommandHelpForTest?
| } | ||
|
|
||
| // NewCommandHelpFromFiles builds a lookup from an existing filename set. | ||
| func NewCommandHelpFromFiles(files map[string]struct{}) *CommandHelp { |
There was a problem hiding this comment.
Suggestion: Maybe use https://pkg.go.dev/golang.org/x/tools/godoc/vfs/mapfs and remove this New function.
There was a problem hiding this comment.
it is deprecated! from your own link :-)
|
|
||
| func docFilesFromEmbed(fsys fs.FS, root string) map[string]struct{} { | ||
| files := make(map[string]struct{}) | ||
| _ = fs.WalkDir(fsys, root, func(_ string, d fs.DirEntry, err error) error { |
There was a problem hiding this comment.
Question: Why not surface the error?
| @@ -2,6 +2,7 @@ package core | |||
|
|
|||
| // !!! This import needs to be the first import, please do not change this !!! | |||
There was a problem hiding this comment.
Issue: please see the comment above!!! the fips import needs to be the first one
|
@robertolopezlopez besides the remaining comments, I think this looks now very good! Thank you for the refactoring! |
e0247a9 to
fa229bf
Compare
PR Reviewer Guide 🔍
|
User description
Follow-up to CLI-1474 / PR #6884 (CLI-1596): replace the generated, committed
manifest.txtwithgo:embedofhelp/cli-commands, removing the drift-prone manifest artifact and thehelpdocs-manifest/git add manifest.txtworkflow.Why this is necessary: GitBook sync updates
help/cli-commands/without triggering a build. A separate committed manifest could drift from the synced markdown and break help routing or CI until someone remembered to regenerate it.What changed technically:
cliv2/internal/helpdocsnow uses//go:embed cli-commandsand walks embedded.mdbasenames (same lookup logic as before), via aCommandHelptype instead of package-level state.helpdocs-manifestis removed;_helpdocs-prepare/_helpdocs-cleancopyhelp/cli-commands/*.mdinto the embed tree at build time only, then remove copied.mdfiles afterward.make -C cliv2 testand lint do not run the copy step.cli-commands/do-not-deletekeeps the embed directory present for tooling (go list, gopls) between builds;cliv2/.gitignoreignores transient copied markdown underinternal/helpdocs/cli-commands/.helpdocsandhelproutinguseCommandHelpFromRepo()(readshelp/cli-commands/from disk) or a small in-memory fixture when that directory is unavailable. NoTestMainsync and no skip guards for barego test.Help routing behavior is unchanged: documented commands still get legacy GitBook markdown help; undocumented ones still get Cobra help. Production wiring uses
helpdocs.DefaultCommandHelp().HasUserDocfrommain.go.Pull Request Submission Checklist
What does this PR do?
cliv2/internal/helpdocs/manifest.txtand manifest sync test.help/cli-commandsmarkdown viago:embedinstead of a basename list.helpdocs-manifestMakefile target with transient_helpdocs-prepare/_helpdocs-cleanaround build only.CommandHelpand wiresHasUserDocthroughhelprouting.Routerandmain.go.CONTRIBUTING.mdfor the new workflow.Where should the reviewer start?
cliv2/internal/helpdocs/command_help.go+embed.go— lookup + compile-time embed (behavior should match pre-change)cliv2/internal/helpdocs/fixture.go— disk/fixture fallback for testscliv2/Makefile—_helpdocs-prepare/_helpdocs-cleanon build target onlycliv2/internal/helprouting/helprouting.go— injectedHasUserDoconRouterHow should this be manually tested?
Verify post-build working tree:
cliv2/internal/helpdocs/cli-commands/contains onlydo-not-delete(no copied.md).What's the product update that needs to be communicated to CLI users?
None. User-facing help content and routing behavior are unchanged.
Risk assessment
Low — same routing algorithm; only the embed data source, build sync, and test wiring changed.
Any background context you want to provide?
go:embedcannot reference repo-roothelp/cli-commands/fromcliv2/internal/helpdocs/(no..paths). The Makefile copies markdown into the package tree at build time; tests read the repo directory directly so they stay in sync with GitBook PRs without a prepare step.What are the relevant tickets?
CLI-1596