@redpanda-data/docs-extensions-and-macros 5.29.0 → 5.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1296,7 +1296,7 @@ node generate-handlebars-docs.js test-input.json test-output
1296
1296
 
1297
1297
  == Additional resources
1298
1298
 
1299
- * https://github.com/redpanda-data/redpanda[Redpanda GitHub Repository]
1299
+ * https://github.com/redpanda-data/streaming-enterprise[streaming-enterprise Repository] (private; this is where the extractor clones Redpanda's source from)
1300
1300
  * https://github.com/redpanda-data/docs-extensions-and-macros[docs-extensions-and-macros Repository]
1301
1301
  * https://tree-sitter.github.io/tree-sitter/[Tree-sitter Documentation]
1302
1302
  * https://handlebarsjs.com/[Handlebars.js Guide]
@@ -6,7 +6,7 @@ This module fetches cloud configuration from the cloudv2 repository to determine
6
6
  which Redpanda properties are supported, editable, or readonly in cloud deployments.
7
7
 
8
8
  Prerequisites:
9
- - GITHUB_TOKEN environment variable set with appropriate permissions
9
+ - GH_TOKEN, GITHUB_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable set with appropriate permissions
10
10
  - Internet connection to access GitHub API
11
11
 
12
12
  Usage:
@@ -105,14 +105,14 @@ def fetch_cloud_config(github_token: Optional[str] = None) -> CloudConfig:
105
105
  """
106
106
  Fetch the latest cloud configuration from the redpanda-data/cloudv2 repository and return it as a CloudConfig.
107
107
 
108
- This function uses a GitHub personal access token for authentication. If `github_token` is not provided, it will read GITHUB_TOKEN or REDPANDA_GITHUB_TOKEN from the environment. It downloads the most recent versioned YAML from the repository's install-pack directory, validates expected sections (`customer_managed_configs` and `readonly_cluster_config`), and constructs a CloudConfig instance.
109
-
108
+ This function uses a GitHub personal access token for authentication. If `github_token` is not provided, it will read GH_TOKEN, GITHUB_TOKEN, or REDPANDA_GITHUB_TOKEN from the environment. It downloads the most recent versioned YAML from the repository's install-pack directory, validates expected sections (`customer_managed_configs` and `readonly_cluster_config`), and constructs a CloudConfig instance.
109
+
110
110
  Parameters:
111
- github_token (Optional[str]): Personal access token for GitHub API. If omitted, the function will try environment variables GITHUB_TOKEN or REDPANDA_GITHUB_TOKEN.
112
-
111
+ github_token (Optional[str]): Personal access token for GitHub API. If omitted, the function will try environment variables GH_TOKEN, GITHUB_TOKEN, or REDPANDA_GITHUB_TOKEN.
112
+
113
113
  Returns:
114
114
  CloudConfig: Parsed cloud configuration for the latest available version.
115
-
115
+
116
116
  Raises:
117
117
  GitHubAuthError: Authentication or access problems with the GitHub API (including 401/403 responses).
118
118
  NetworkError: Network connectivity or timeout failures when contacting the GitHub API.
@@ -120,7 +120,11 @@ def fetch_cloud_config(github_token: Optional[str] = None) -> CloudConfig:
120
120
  CloudConfigError: Generic configuration error (for example, missing token) or unexpected internal failures.
121
121
  """
122
122
  if not github_token:
123
- github_token = os.environ.get('GITHUB_TOKEN') or os.environ.get('REDPANDA_GITHUB_TOKEN')
123
+ # GH_TOKEN takes priority: `doc-tools generate property-docs` pre-resolves
124
+ # a token via cli-utils/github-token.js's full priority chain (GIT_CREDENTIALS,
125
+ # ACTIONS_BOT_TOKEN, etc.) and passes it down as GH_TOKEN, so this must see
126
+ # that name too -- not just the two env vars a directly-invoked `make` sets.
127
+ github_token = os.environ.get('GH_TOKEN') or os.environ.get('GITHUB_TOKEN') or os.environ.get('REDPANDA_GITHUB_TOKEN')
124
128
 
125
129
  if not github_token:
126
130
  error_msg = (
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ /**
5
+ * Overwrite cluster/broker scope in an already-extracted --enhanced-output
6
+ * file with rp_util's runtime-introspected data, in place.
7
+ *
8
+ * Called from the Makefile's `build` target, after property_extractor.py's
9
+ * Tree-sitter extraction and before generate-docs renders the result. Topic
10
+ * properties (rp_util has no equivalent) and anything rp_util doesn't cover
11
+ * pass through untouched -- see rp_util_merge.py.
12
+ *
13
+ * Failure behavior depends on what kind of ref is being built:
14
+ *
15
+ * Branch refs (dev, feature branches): never fails the build. If rp_util's
16
+ * schema can't be obtained (no published release for this ref, no GitHub
17
+ * token, no Docker/Bazel available to build from source) or the merge
18
+ * itself errors, this logs a warning and leaves the Tree-sitter-only
19
+ * extraction as the enhanced file, exactly as it already was before this
20
+ * step ran. In-progress content tolerates the degraded data; it gets
21
+ * regenerated constantly.
22
+ *
23
+ * Release tags (v26.3.0, v26.3.1-rc1, ...): fails the build. A release run
24
+ * is the one that publishes the property reference, and shipping the
25
+ * Tree-sitter-only extraction there is not a degradation readers can see
26
+ * past: post streaming-enterprise#63, it silently drops enum_set
27
+ * properties (http_authentication) and misparses others behind a green
28
+ * job. The fix is to publish the schema first (the publish-rp-util-schema
29
+ * workflow, which also runs on a schedule) and re-run; --skip-rp-util /
30
+ * SKIP_RP_UTIL=1 remains the explicit opt-out.
31
+ *
32
+ * Usage:
33
+ * node merge-rp-util.js --tag <ref> --enhanced <path> [--overrides <path>] [--output <path>]
34
+ */
35
+
36
+ const fs = require('fs')
37
+ const os = require('os')
38
+ const path = require('path')
39
+ const { spawnSync } = require('child_process')
40
+ const { getRpUtilSchema, SCHEMA_FLAGS } = require('./rp-util-fetch')
41
+ const bigIntJson = require('../../cli-utils/big-int-json')
42
+
43
+ function getArg(args, flag) {
44
+ const i = args.indexOf(flag)
45
+ return i === -1 ? undefined : args[i + 1]
46
+ }
47
+
48
+ // A ref this pipeline treats as a release build: vX.Y.Z, with or without an
49
+ // -rcN suffix (RC tags feed the docs beta branch, which publishes too). Same
50
+ // release/RC shape streaming-enterprise's own dispatch-docs-updates.yml keys
51
+ // on. Everything else (dev, feature branches, bare SHAs) is in-progress
52
+ // content where a degraded merge is tolerable.
53
+ const RELEASE_TAG_RX = /^v?\d+\.\d+\.\d+(-rc\d+)?$/
54
+
55
+ /**
56
+ * Handle a step of the rp_util merge being unavailable: fatal for a release
57
+ * tag (see the header comment), a warning plus the Tree-sitter fallback for
58
+ * everything else.
59
+ */
60
+ function handleMergeUnavailable(tag, enhanced, reason) {
61
+ if (RELEASE_TAG_RX.test(tag)) {
62
+ // Print the canonical v-prefixed tag in the remediation command:
63
+ // streaming-enterprise tags (and therefore schema release names) are
64
+ // always v-prefixed, so telling the operator to publish a v-less name
65
+ // would send them to a workflow run that can never help.
66
+ const releaseTag = tag.startsWith('v') ? tag : `v${tag}`
67
+ console.error(`Error: ${reason}`)
68
+ console.error(
69
+ `Refusing to publish Tree-sitter-only property data for release ${tag}: ` +
70
+ 'it drops or misparses enum_set properties. Publish the rp_util schema ' +
71
+ `first (gh workflow run publish-rp-util-schema.yaml -f tag=${releaseTag}, or wait ` +
72
+ 'for its scheduled run) and retry, or pass --skip-rp-util to override.'
73
+ )
74
+ process.exitCode = 1
75
+ return
76
+ }
77
+ console.warn(`Warning: ${reason}`)
78
+ console.warn('Skipping rp_util merge -- keeping the Tree-sitter-only extraction for cluster/broker scope.')
79
+ markRpUtilMergeUnavailable(enhanced)
80
+ }
81
+
82
+ // rp_util covers every cluster/broker-scope property (see this file's own
83
+ // header comment), so when its merge is skipped or fails, any cluster/
84
+ // broker property still missing gets_restored isn't "no annotation exists"
85
+ // (the pre-existing, legitimate case property.hbs already renders as an
86
+ // absent row) -- it's "we don't know, because the merge that would have
87
+ // told us didn't run". Mark those explicitly so the template can render
88
+ // that as a visible "Unknown" state instead of silently rendering nothing,
89
+ // which previously made a flaky rp_util fetch indistinguishable from a
90
+ // real upstream change in the generated docs.
91
+ const RP_UTIL_COVERED_SCOPES = new Set(['cluster', 'broker'])
92
+
93
+ function markRpUtilMergeUnavailable(enhancedPath) {
94
+ let data
95
+ try {
96
+ data = bigIntJson.parse(fs.readFileSync(enhancedPath, 'utf8'))
97
+ } catch (err) {
98
+ console.warn(`Warning: could not mark rp_util merge as unavailable in ${enhancedPath}: ${err.message}`)
99
+ return
100
+ }
101
+ const properties = data.properties || {}
102
+ let marked = 0
103
+ for (const prop of Object.values(properties)) {
104
+ if (!prop || !RP_UTIL_COVERED_SCOPES.has(prop.config_scope)) continue
105
+ if (prop.gets_restored !== undefined) continue
106
+ prop.rp_util_merge_status = 'unavailable'
107
+ marked++
108
+ }
109
+ if (marked === 0) return
110
+ try {
111
+ fs.writeFileSync(enhancedPath, bigIntJson.stringify(data))
112
+ } catch (err) {
113
+ console.warn(`Warning: could not write rp_util-unavailable marker to ${enhancedPath}: ${err.message}`)
114
+ }
115
+ }
116
+
117
+ async function main() {
118
+ const args = process.argv.slice(2)
119
+ const tag = getArg(args, '--tag')
120
+ const enhanced = getArg(args, '--enhanced')
121
+ const overrides = getArg(args, '--overrides')
122
+ const output = getArg(args, '--output') || enhanced
123
+ const sourcePath = getArg(args, '--source-path')
124
+ // Cluster properties rp_util reports but the Tree-sitter pass never saw
125
+ // have no existing entry to carry cloud metadata forward from, so the
126
+ // merge needs its own CloudConfig to annotate them -- see rp_util_merge.py.
127
+ // Honours CLOUD_SUPPORT=1 as well because that is how doc-tools passes the
128
+ // request down to `make` in the first place.
129
+ const cloudSupport = args.includes('--cloud-support') || process.env.CLOUD_SUPPORT === '1'
130
+
131
+ if (!tag || !enhanced) {
132
+ console.error(
133
+ 'Usage: node merge-rp-util.js --tag <ref> --enhanced <path> ' +
134
+ '[--overrides <path>] [--output <path>] [--source-path <dir>] [--cloud-support]'
135
+ )
136
+ process.exit(1)
137
+ }
138
+
139
+ console.log(`Fetching rp_util schema for ${tag}...`)
140
+ let schemas
141
+ try {
142
+ // --source-path builds from an existing local streaming-enterprise
143
+ // checkout instead of fetching a published release or cloning+building
144
+ // fresh -- same option rp-util-fetch.js itself exposes, useful for
145
+ // local iteration against a branch that has no published schema yet.
146
+ schemas = await getRpUtilSchema(tag, sourcePath ? { sourcePath } : undefined)
147
+ } catch (err) {
148
+ handleMergeUnavailable(tag, enhanced, `could not get rp_util schema for ${tag}: ${err.message}`)
149
+ return
150
+ }
151
+
152
+ const schemaDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rp-util-schemas-'))
153
+ try {
154
+ let anySchema = false
155
+ for (const { key } of SCHEMA_FLAGS) {
156
+ if (schemas[key]) {
157
+ fs.writeFileSync(path.join(schemaDir, `${key}.json`), bigIntJson.stringify(schemas[key]))
158
+ anySchema = true
159
+ }
160
+ }
161
+ if (!anySchema) {
162
+ handleMergeUnavailable(tag, enhanced, `rp_util schema for ${tag} came back empty.`)
163
+ return
164
+ }
165
+
166
+ const venvPython = path.join(__dirname, 'tmp', 'redpanda-property-extractor-venv', 'bin', 'python')
167
+ const pythonBin = fs.existsSync(venvPython) ? venvPython : 'python3'
168
+
169
+ // Write to a temp path and only rename over the real output on success.
170
+ // output frequently equals enhanced (an in-place update), so a partial
171
+ // write from a mid-json.dump failure must never be able to leave that
172
+ // file truncated -- generate-docs reads it right after this returns.
173
+ const tempOutput = `${output}.rp-util-merge-tmp`
174
+ const mergeArgs = [
175
+ 'rp_util_merge.py',
176
+ '--enhanced', enhanced,
177
+ '--rp-util-dir', schemaDir,
178
+ '--output', tempOutput
179
+ ]
180
+ if (overrides && fs.existsSync(overrides)) {
181
+ mergeArgs.push('--overrides', overrides)
182
+ }
183
+ if (cloudSupport) {
184
+ mergeArgs.push('--cloud-support')
185
+ }
186
+
187
+ console.log('Merging rp_util schema into extracted properties...')
188
+ const result = spawnSync(pythonBin, mergeArgs, { cwd: __dirname, stdio: 'inherit' })
189
+ if (result.error || result.status !== 0) {
190
+ fs.rmSync(tempOutput, { force: true })
191
+ handleMergeUnavailable(
192
+ tag,
193
+ enhanced,
194
+ `rp_util merge failed (${result.error ? result.error.message : `exit ${result.status}`}).`
195
+ )
196
+ } else {
197
+ fs.renameSync(tempOutput, output)
198
+ }
199
+ } finally {
200
+ fs.rmSync(schemaDir, { recursive: true, force: true })
201
+ }
202
+ }
203
+
204
+ module.exports = { main, markRpUtilMergeUnavailable }
205
+
206
+ if (require.main === module) {
207
+ main()
208
+ }