Skip to content

Commit a2c9ab7

Browse files
authored
Merge pull request #65 from salesforcecli/jo_gen_lineage
lineage generation
2 parents c5226cc + 7afa713 commit a2c9ab7

20 files changed

Lines changed: 2901 additions & 37 deletions

‎messages/datacodeBinaryExecutor.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ Entrypoint file not found at '%s'.
1818
- Run 'init' first if the package has not been initialized
1919
- Use the default entrypoint path: payload/entrypoint.py
2020

21+
# error.scanConfigChanged
22+
23+
'%s' changed while waiting for confirmation, so the scan results were not written.
24+
25+
# actions.scanConfigChanged
26+
27+
- Re-run the scan so it starts from the current config.json.
28+
2129
# error.zipExecutionFailed
2230

2331
Failed to create archive for package at '%s': %s

‎messages/lineage.md‎

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# summary
2+
3+
Generate field-level dataLineage for a Data Code Extension %s package.
4+
5+
# description
6+
7+
Runs the package's entrypoint against a Salesforce org with its Data Cloud writes intercepted, traces each written field back to the fields it was computed from, and replaces the dataLineage section of the package's config.json with the result. Nothing is written to Data Cloud objects, but other calls the script makes still run for real with the org's credentials, including named credential requests, LLM gateway calls, and Einstein predictions. Streaming scripts aren't supported. Run 'scan' first so the read and write permissions in config.json are current. Fields whose lineage can't be traced are listed and get no lineage unless you add entries by hand.
8+
9+
Generated lineage is a best-effort analysis of your script and may be incomplete or wrong (for example, for UDFs or other code the tool can't see into). It determines how governance tags propagate, which can restrict or open access to data. You're responsible for reviewing and maintaining it. Before config.json is written, you must accept this, either at the prompt or with --accept-lineage-ownership.
10+
11+
# examples.script
12+
13+
- Generate lineage for a script package against the org with alias "myorg":
14+
15+
<%= config.bin %> data-code-extension script lineage generate --entrypoint ./my-script-package/payload/entrypoint.py --target-org myorg
16+
17+
- Preview the generated lineage without changing config.json:
18+
19+
<%= config.bin %> data-code-extension script lineage generate --entrypoint ./my-script-package/payload/entrypoint.py --target-org myorg --dry-run
20+
21+
- Accept ownership of the generated lineage and replace existing lineage without prompting, keeping the full result for review:
22+
23+
<%= config.bin %> data-code-extension script lineage generate --entrypoint ./my-script-package/payload/entrypoint.py --target-org myorg --accept-lineage-ownership --no-prompt --output-file ./lineage-result.json
24+
25+
# info.checkingPython
26+
27+
Checking Python version...
28+
29+
# info.pythonFound
30+
31+
Python %s found at '%s'
32+
33+
# info.checkingPackages
34+
35+
Checking required Python packages...
36+
37+
# info.packageFound
38+
39+
Package '%s' version %s found
40+
41+
# info.authenticating
42+
43+
Authenticating with Salesforce org '%s'...
44+
45+
# info.authenticated
46+
47+
Successfully authenticated with org '%s'
48+
49+
# info.generating
50+
51+
Running package to trace lineage. Data Cloud writes are intercepted; other external calls the script makes still run...
52+
53+
# info.summary
54+
55+
Generated %s dataLineage entries with %s (target, source) field pairs: +%s added, -%s removed, ~%s changed.
56+
57+
# info.removed
58+
59+
Entries in config.json that the generated lineage drops:
60+
%s
61+
62+
# info.changed
63+
64+
Entries in config.json whose source fields the generated lineage changes:
65+
%s
66+
67+
# info.noSources
68+
69+
%s target fields are computed from no source field (for example, literals) and get no lineage:
70+
%s
71+
72+
# info.outputWritten
73+
74+
Wrote the full lineage result to '%s'.
75+
76+
# info.dryRunNotice
77+
78+
DRY RUN: config.json was not modified. Remove --dry-run to apply the generated lineage.
79+
80+
# info.unchanged
81+
82+
dataLineage in '%s' is already up to date.
83+
84+
# info.written
85+
86+
Updated dataLineage in '%s'.
87+
88+
# info.notWritten
89+
90+
Generated lineage was not accepted; config.json was not modified.
91+
92+
# info.complete
93+
94+
Lineage generation completed successfully!
95+
96+
# warning.unresolved
97+
98+
%s target fields could not be traced and get no lineage unless you add entries by hand:
99+
%s
100+
101+
# warning.udf
102+
103+
%s target fields are computed by user-defined functions (UDFs). Their lineage lists only the fields passed to the UDF; the tool can't see what else the UDF reads (for example, a lookup table, a broadcast variable, or a file). Review these fields and add any missing source fields by hand:
104+
%s
105+
106+
# warning.permissionGaps
107+
108+
The generated lineage references objects that aren't in config.json permissions, so deploy would fail (1041/1042). Run 'data-code-extension script scan' first, then generate lineage again. Scan can't detect objects whose names your script builds at run time; add those to permissions in config.json by hand.
109+
%s
110+
111+
# warning.emptyResult
112+
113+
No lineage could be generated, so config.json was not modified. The package follows the no-lineage path unless you add dataLineage entries by hand.
114+
115+
# warning.emptyResultExisting
116+
117+
No lineage could be generated, so config.json was not modified. Its existing %s dataLineage entries still apply and are deployed with the package. Review or remove them in '%s'.
118+
119+
# warning.replace
120+
121+
This replaces the existing dataLineage section. Any entries you added or edited by hand will be lost. Lineage determines how governance tags propagate to the target; review the result before deploying.
122+
123+
# prompt.replace
124+
125+
Replace the existing dataLineage in config.json?
126+
127+
# warning.ownership
128+
129+
Generated lineage is a best-effort analysis of your script and may be incomplete or wrong (for example, for UDFs or other code the tool can't see into). It determines how governance tags propagate, which can restrict or open access to data. You're responsible for reviewing and maintaining it.
130+
131+
# prompt.acceptOwnership
132+
133+
Accept and write dataLineage to config.json?
134+
135+
# prompt.acceptOwnershipReplace
136+
137+
Accept and replace the existing dataLineage in config.json?
138+
139+
# error.functionPackage
140+
141+
Lineage can only be generated for script packages; '%s' is a function package.
142+
143+
# error.streamingPackage
144+
145+
Lineage can't be generated for streaming scripts; '%s' has a streamingSource in config.json.
146+
147+
# error.configUnwritable
148+
149+
Can't write '%s': %s
150+
151+
# error.outputFileCollision
152+
153+
--output-file '%s' is inside the package's payload or project metadata, or is one of its config files. Choose a path outside the package's payload directory.
154+
155+
# error.lineageChanged
156+
157+
'%s' changed while waiting for confirmation, so dataLineage was not replaced.
158+
159+
# actions.lineageChanged
160+
161+
- Re-run the command to compare the generated lineage against the current config.json.
162+
163+
# error.outputFileUnwritable
164+
165+
Can't write --output-file '%s': %s
166+
167+
# error.noUsername
168+
169+
The target org has no username to authenticate the lineage run with.
170+
171+
# error.ownershipNotAccepted
172+
173+
Writing generated lineage to config.json requires accepting ownership of it.
174+
175+
# actions.ownershipNotAccepted
176+
177+
- Re-run with --accept-lineage-ownership to accept responsibility for the generated lineage and write it to config.json.
178+
- Re-run with --dry-run to preview the generated lineage, or check it against config.json, without writing.
179+
180+
# error.replaceNotConfirmed
181+
182+
config.json already has a different dataLineage section, and replacing it needs confirmation.
183+
184+
# actions.replaceNotConfirmed
185+
186+
- Re-run with --no-prompt to replace the existing dataLineage.
187+
- Re-run with --dry-run to preview the generated lineage, or check it against config.json, without writing.
188+
189+
# error.sdkTooOld
190+
191+
The installed salesforce-data-customcode package doesn't support lineage generation.
192+
193+
# actions.sdkTooOld
194+
195+
- Upgrade the SDK with 'pip install -U salesforce-data-customcode', then try again.
196+
197+
# error.resultMissing
198+
199+
The lineage run finished but produced no result: %s
200+
201+
# error.resultInvalid
202+
203+
The lineage run produced a result this CLI can't read: %s
204+
205+
# actions.resultInvalid
206+
207+
- Upgrade the SDK with 'pip install -U salesforce-data-customcode', then try again.
208+
209+
# actions.resultMissing
210+
211+
- Review the output above for errors from the entrypoint.
212+
- Upgrade the SDK with 'pip install -U salesforce-data-customcode', then try again.
213+
214+
# flags.entrypoint.summary
215+
216+
Entrypoint file for the package to generate lineage for.
217+
218+
# flags.entrypoint.description
219+
220+
The path to the entrypoint file of your initialized Data Cloud custom code script package. The package's config.json, in the same directory, is updated with the generated lineage.
221+
222+
# flags.targetOrg.summary
223+
224+
Target Salesforce org to read data from.
225+
226+
# flags.targetOrg.description
227+
228+
The alias or username of the Salesforce org whose data the package reads while lineage is traced. The org must have Data Cloud enabled and appropriate permissions. Nothing is written to Data Cloud objects in the org, but other calls the script makes, such as named credential requests, LLM gateway calls, and Einstein predictions, still run against it.
229+
230+
# flags.configFile.summary
231+
232+
Path to an SDK config file.
233+
234+
# flags.configFile.description
235+
236+
Optional path to an SDK config.yaml that overrides the reader and writer configuration for the run, for example the reader's default_row_limit (1000 rows by default).
237+
238+
# flags.dependencies.summary
239+
240+
Modules to import before the run.
241+
242+
# flags.dependencies.description
243+
244+
Optional comma-separated list of Python modules (or module.attribute) to import before the entrypoint runs. Use import names, such as 'yaml' rather than 'pyyaml'. Nothing is installed: each module must already be installed in the active Python environment.
245+
246+
# flags.dryRun.summary
247+
248+
Print the generated lineage without modifying config.json.
249+
250+
# flags.dryRun.description
251+
252+
When set, generates and prints the lineage but doesn't modify config.json and never prompts.
253+
254+
# flags.outputFile.summary
255+
256+
Also write the full lineage result to this JSON file.
257+
258+
# flags.outputFile.description
259+
260+
Writes the full result, including unresolved fields, targets, sources, and the pair count, to the given path. Useful for review or CI.
261+
262+
# flags.noPrompt.summary
263+
264+
Replace existing dataLineage without prompting.
265+
266+
# flags.noPrompt.description
267+
268+
Skips the confirmation before replacing a dataLineage section that differs from the generated one. Required when running non-interactively or with --json and config.json already has dataLineage; checked before the run. Doesn't accept ownership of the generated lineage; use --accept-lineage-ownership for that.
269+
270+
# flags.acceptLineageOwnership.summary
271+
272+
Accept responsibility for the generated lineage without prompting.
273+
274+
# flags.acceptLineageOwnership.description
275+
276+
Confirms that you understand the generated lineage is best-effort and may be incomplete or wrong, that it determines how governance tags propagate, and that you're responsible for reviewing and maintaining it. Skips the ownership prompt. Required when running non-interactively or with --json, unless you use --dry-run; checked before the run.

‎messages/run.md‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ Authentication with Salesforce org '%s' failed while running the package.
9898
- Confirm the org alias or username passed to --target-org is correct.
9999
- Verify the org has Data Cloud enabled and your user has the required permissions.
100100

101+
# error.runTerminated
102+
103+
The package run was stopped by signal %s before it finished. Runs time out after %s seconds.
104+
105+
# actions.runTerminated
106+
107+
- If the run timed out, reduce the data it processes (for example, the reader's default_row_limit in an SDK config file) or check for a step that hangs, such as a network call.
108+
- If the process ran out of memory or was stopped by another program, free resources and try again.
109+
101110
# error.runProcessStartFailed
102111

103112
Could not start the Python interpreter '%s': %s
@@ -149,8 +158,8 @@ Optional path to a JSON config file that provides input payload for the run. Def
149158

150159
# flags.dependencies.summary
151160

152-
Dependencies override for the run.
161+
Modules to import before the run.
153162

154163
# flags.dependencies.description
155164

156-
Optional comma-separated list of Python package dependencies to use during the run, overriding those defined in the package's requirements.txt.
165+
Optional comma-separated list of Python modules (or module.attribute) to import before the entrypoint runs. Use import names, such as 'yaml' rather than 'pyyaml'. Nothing is installed: each module must already be installed in the active Python environment.

‎messages/scan.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,34 @@ Checking required Python packages...
101101
# info.packageFound
102102

103103
Package '%s' version %s found
104+
105+
# flags.noPrompt.summary
106+
107+
Write config.json without prompting when dataLineage is stale.
108+
109+
# flags.noPrompt.description
110+
111+
Skips the confirmation scan asks for when the rebuilt permissions no longer cover objects that dataLineage references. Required to write config.json in that case when running non-interactively.
112+
113+
# warning.staleLineage
114+
115+
dataLineage references objects that are no longer in permissions (%s entries affected). The package will fail to deploy (1041/1042) until you rerun 'data-code-extension script lineage generate' or edit dataLineage. Scan doesn't change dataLineage.
116+
%s
117+
118+
# prompt.staleLineage
119+
120+
Write the updated permissions to config.json anyway?
121+
122+
# error.staleLineageNotWritten
123+
124+
The updated permissions weren't confirmed, so no files were modified. Update dataLineage, then scan again.
125+
126+
# error.staleLineageNotConfirmed
127+
128+
dataLineage references objects that are no longer in permissions, and writing config.json needs confirmation.
129+
130+
# actions.staleLineageNotConfirmed
131+
132+
- Re-run 'data-code-extension script lineage generate' to regenerate dataLineage, or edit it by hand.
133+
- Re-run scan with --no-prompt to write the updated permissions anyway.
134+
- Re-run scan with --dry-run to preview the changes.

‎package.json‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@
5050
"description": "Commands for Data Cloud Code Extension.",
5151
"subtopics": {
5252
"script": {
53-
"description": "Commands to initialize and manage script-based Data Cloud packages."
53+
"description": "Commands to initialize and manage script-based Data Cloud packages.",
54+
"subtopics": {
55+
"lineage": {
56+
"description": "Commands to generate field-level data lineage for script-based Data Cloud packages.",
57+
"hidden": true
58+
}
59+
}
5460
},
5561
"function": {
5662
"description": "Commands to initialize and manage function-based Data Cloud packages."

0 commit comments

Comments
 (0)