Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,23 @@ jobs:
mkdir -p _output
echo "$MERGED_MANIFEST" | jq . > _output/manifest.json

- name: Compile documentation to HTML
id: compile-docs
if: steps.read-docs.outputs.has_docs == 'true'
working-directory: _workflows
run: |
cd cmd/record-release && npm ci --ignore-scripts 2>/dev/null
cd ../..
node cmd/record-release/mdx-compile.mjs < ../_connector/docs/connector.mdx > /tmp/docs.html 2>/dev/null
if [ -s /tmp/docs.html ]; then
echo "has_html=true" >> "$GITHUB_OUTPUT"
echo "Compiled docs to HTML ($(wc -c < /tmp/docs.html) bytes)"
else
echo "has_html=false" >> "$GITHUB_OUTPUT"
echo "MDX compilation produced empty output, skipping HTML"
fi
continue-on-error: true

- name: Record release via registry API
if: steps.registry-oidc.outcome == 'success'
working-directory: _workflows
Expand All @@ -1342,6 +1359,11 @@ jobs:
DOCS_FLAG="-docs ../_connector/docs/connector.mdx"
fi

DOCS_HTML_FLAG=""
if [ "${{ steps.compile-docs.outputs.has_html }}" = "true" ]; then
DOCS_HTML_FLAG="-docs-html /tmp/docs.html"
fi

CHANGELOG_FLAG=""
if [ -s /tmp/changelog.md ]; then
CHANGELOG_FLAG="-changelog /tmp/changelog.md"
Expand Down Expand Up @@ -1372,6 +1394,7 @@ jobs:
-workflow-run-id "${{ github.run_id }}" \
-registry-url "https://dist.conductorone.com" \
$DOCS_FLAG \
$DOCS_HTML_FLAG \
$CHANGELOG_FLAG \
$CONFIG_SCHEMA_FLAG \
$CAPABILITIES_FLAG \
Expand Down
91 changes: 53 additions & 38 deletions cmd/record-release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,22 @@ import (

// RecordReleaseRequest is the JSON body sent to the registry API.
type RecordReleaseRequest struct {
Org string `json:"org"`
Name string `json:"name"`
Version string `json:"version"`
RepositoryURL string `json:"repositoryUrl"`
CommitSha string `json:"commitSha"`
WorkflowRunID string `json:"workflowRunId"`
Documentation string `json:"documentation,omitempty"`
Changelog string `json:"changelog,omitempty"`
ConfigSchema string `json:"configSchema,omitempty"`
Capabilities string `json:"capabilities,omitempty"`
SignatureURL string `json:"signatureUrl,omitempty"`
CertificateURL string `json:"certificateUrl,omitempty"`
Assets map[string]*ReleaseAsset `json:"assets,omitempty"`
Images map[string]*ReleaseImage `json:"images,omitempty"`
ReleasedAt string `json:"releasedAt,omitempty"`
Org string `json:"org"`
Name string `json:"name"`
Version string `json:"version"`
RepositoryURL string `json:"repositoryUrl"`
CommitSha string `json:"commitSha"`
WorkflowRunID string `json:"workflowRunId"`
Documentation string `json:"documentation,omitempty"`
DocumentationHTML string `json:"documentationHtml,omitempty"`
Changelog string `json:"changelog,omitempty"`
ConfigSchema string `json:"configSchema,omitempty"`
Capabilities string `json:"capabilities,omitempty"`
SignatureURL string `json:"signatureUrl,omitempty"`
CertificateURL string `json:"certificateUrl,omitempty"`
Assets map[string]*ReleaseAsset `json:"assets,omitempty"`
Images map[string]*ReleaseImage `json:"images,omitempty"`
ReleasedAt string `json:"releasedAt,omitempty"`
}

// ReleaseAsset is the transformed asset for the registry API.
Expand Down Expand Up @@ -71,14 +72,15 @@ func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) {

func main() {
var (
manifestPath string
docsPath string
org string
name string
version string
repositoryURL string
commitSha string
workflowRunID string
manifestPath string
docsPath string
docsHTMLPath string
org string
name string
version string
repositoryURL string
commitSha string
workflowRunID string
registryURL string
changelogPath string
configSchemaPath string
Expand All @@ -88,6 +90,7 @@ func main() {

flag.StringVar(&manifestPath, "manifest", "", "Path to merged manifest.json file (required)")
flag.StringVar(&docsPath, "docs", "", "Path to docs/connector.mdx file (optional)")
flag.StringVar(&docsHTMLPath, "docs-html", "", "Path to pre-compiled HTML documentation file (optional)")
flag.StringVar(&org, "org", "", "GitHub organization (required)")
flag.StringVar(&name, "name", "", "Repository/connector name (required)")
flag.StringVar(&version, "version", "", "Release version tag (required)")
Expand Down Expand Up @@ -172,6 +175,17 @@ func main() {
}
}

// Read optional pre-compiled HTML documentation
var documentationHTML string
if docsHTMLPath != "" {
htmlBytes, err := os.ReadFile(docsHTMLPath)
if err != nil {
fmt.Fprintf(os.Stderr, "record-release: warning: could not read docs-html file: %v\n", err)
} else {
documentationHTML = string(htmlBytes)
}
}

// Read optional changelog / release notes
var changelog string
if changelogPath != "" {
Expand Down Expand Up @@ -234,21 +248,22 @@ func main() {

// Build request body
req := &RecordReleaseRequest{
Org: org,
Name: name,
Version: version,
RepositoryURL: repositoryURL,
CommitSha: commitSha,
WorkflowRunID: workflowRunID,
Documentation: documentation,
Changelog: changelog,
ConfigSchema: configSchema,
Capabilities: capabilities,
SignatureURL: manifest.GetSignatureHref(),
CertificateURL: manifest.GetCertificateHref(),
Assets: assets,
Images: images,
ReleasedAt: releasedAt,
Org: org,
Name: name,
Version: version,
RepositoryURL: repositoryURL,
CommitSha: commitSha,
WorkflowRunID: workflowRunID,
Documentation: documentation,
DocumentationHTML: documentationHTML,
Changelog: changelog,
ConfigSchema: configSchema,
Capabilities: capabilities,
SignatureURL: manifest.GetSignatureHref(),
CertificateURL: manifest.GetCertificateHref(),
Assets: assets,
Images: images,
ReleasedAt: releasedAt,
}

bodyBytes, err := json.Marshal(req)
Expand Down
204 changes: 204 additions & 0 deletions cmd/record-release/mdx-compile.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#!/usr/bin/env node
//
// MDX-to-HTML compiler for connector documentation.
//
// Reads MDX from stdin, compiles it to static HTML using the same Mintlify
// component mappings as the registry UI, writes HTML to stdout.
//
// Usage:
// cat docs/connector.mdx | node ui/mdx-compile.mjs
// echo "$MDX_CONTENT" | node ui/mdx-compile.mjs
//
// Exit codes:
// 0 - success (HTML on stdout)
// 1 - compilation error (message on stderr)

import { compile, run } from "@mdx-js/mdx";
import * as runtime from "react/jsx-runtime";
import React from "react";
import ReactDOMServer from "react-dom/server";
import remarkGfm from "remark-gfm";
import remarkFrontmatter from "remark-frontmatter";

// ── Mintlify component mappings (HTML equivalents) ──────────────────

function Tip({ children }) {
return React.createElement(
"div",
{ className: "mdx-alert mdx-alert-tip" },
React.createElement("div", { className: "mdx-alert-icon" }, "\u2139\uFE0F"),
React.createElement("div", { className: "mdx-alert-content" }, children),
);
}

function Warning({ children }) {
return React.createElement(
"div",
{ className: "mdx-alert mdx-alert-warning" },
React.createElement("div", { className: "mdx-alert-icon" }, "\u26A0\uFE0F"),
React.createElement("div", { className: "mdx-alert-content" }, children),
);
}

function Note({ children }) {
return React.createElement(
"div",
{ className: "mdx-alert mdx-alert-note" },
React.createElement("div", { className: "mdx-alert-icon" }, "\u2139\uFE0F"),
React.createElement("div", { className: "mdx-alert-content" }, children),
);
}

function Icon({ icon, color }) {
if (icon === "square-check") {
return React.createElement(
"span",
{
className: "mdx-icon mdx-icon-check",
style: { color: color || "#4caf50" },
},
"\u2611",
);
}
return null;
}

function Frame({ children, caption }) {
return React.createElement(
"div",
{ className: "mdx-frame" },
children,
caption
? React.createElement(
"div",
{ className: "mdx-frame-caption" },
caption,
)
: null,
);
}

function Card({ children, title }) {
return React.createElement(
"div",
{ className: "mdx-card" },
title ? React.createElement("h4", null, title) : null,
children,
);
}

function Tabs({ children }) {
const childArray = Array.isArray(children) ? children : [children];
const tabs = childArray.filter((c) => c?.props?.title);

if (tabs.length === 0) return React.createElement(React.Fragment, null, children);

return React.createElement(
"div",
{ className: "mdx-tabs" },
React.createElement(
"div",
{ className: "mdx-tabs-nav", role: "tablist" },
tabs.map((tab, i) =>
React.createElement(
"button",
{
key: i,
className: `mdx-tab-btn${i === 0 ? " mdx-tab-active" : ""}`,
"data-tab-index": i,
role: "tab",
type: "button",
},
tab.props.title,
),
),
),
tabs.map((tab, i) =>
React.createElement(
"div",
{
key: i,
className: `mdx-tab-panel${i === 0 ? " mdx-tab-visible" : ""}`,
"data-tab-index": i,
role: "tabpanel",
},
tab.props.children,
),
),
);
}

function Tab({ children }) {
return React.createElement(React.Fragment, null, children);
}

function Steps({ children }) {
const childArray = Array.isArray(children) ? children : [children];
const steps = childArray.filter((c) => c?.props);
return React.createElement(
"ol",
{ className: "mdx-steps" },
steps.map((child, i) =>
React.createElement(
"li",
{ key: i, className: "mdx-step" },
child?.props?.children,
),
),
);
}

function Step({ children }) {
return React.createElement(React.Fragment, null, children);
}

// ── Component map ───────────────────────────────────────────────────

const components = {
Tip,
Warning,
Note,
Icon,
Frame,
Card,
Tabs,
Tab,
Steps,
Step,
};

// ── Main ────────────────────────────────────────────────────────────

async function main() {
let content = "";
for await (const chunk of process.stdin) {
content += chunk;
}

if (!content.trim()) {
process.exit(0);
}

try {
const compiled = await compile(content, {
outputFormat: "function-body",
remarkPlugins: [remarkGfm, remarkFrontmatter],
});

const { default: MDXContent } = await run(String(compiled), {
...runtime,
baseUrl: "file:///",
});

const html = ReactDOMServer.renderToStaticMarkup(
React.createElement(MDXContent, { components }),
);

process.stdout.write(html);
} catch (err) {
process.stderr.write(`mdx-compile: ${err.message}\n`);
process.exit(1);
}
}

main();
11 changes: 11 additions & 0 deletions cmd/record-release/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"private": true,
"type": "module",
"dependencies": {
"@mdx-js/mdx": "^3.1.1",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1"
}
}