first commit
This commit is contained in:
286
.github/workflows/scripts/push-mintlify-changelog.sh
vendored
Executable file
286
.github/workflows/scripts/push-mintlify-changelog.sh
vendored
Executable file
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=$1
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Usage: $0 <version>"
|
||||
echo "Example: $0 1.2.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="v$VERSION"
|
||||
|
||||
# Check if this page already exists in docs/changelogs/
|
||||
if [ -f "docs/changelogs/$VERSION.mdx" ]; then
|
||||
echo "✅ Changelog for $VERSION already exists"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Source changelog utilities
|
||||
source "$(dirname "$0")/changelog-utils.sh"
|
||||
|
||||
# Get current date
|
||||
CURRENT_DATE=$(date +"%Y-%m-%d")
|
||||
|
||||
# Preparing changelog file
|
||||
CHANGELOG_BODY="---
|
||||
title: \"$VERSION\"
|
||||
description: \"$VERSION changelog - $CURRENT_DATE\"
|
||||
---
|
||||
<Tabs>
|
||||
<Tab title=\"NPX\">
|
||||
\`\`\`bash
|
||||
npx -y @maximhq/bifrost --transport-version $VERSION
|
||||
\`\`\`
|
||||
</Tab>
|
||||
<Tab title=\"Docker\">
|
||||
\`\`\`bash
|
||||
docker pull maximhq/bifrost:$VERSION
|
||||
docker run -p 8080:8080 maximhq/bifrost:$VERSION
|
||||
\`\`\`
|
||||
</Tab>
|
||||
</Tabs>
|
||||
"
|
||||
|
||||
# Array to track cleaned changelog files
|
||||
CLEANED_CHANGELOG_FILES=()
|
||||
|
||||
# Helper to append a section if changelog file exists and is non-empty
|
||||
append_section () {
|
||||
label=$1
|
||||
path=$2
|
||||
if [ -f "$path" ]; then
|
||||
# Get changelog content
|
||||
content=$(get_file_content "$path")
|
||||
# If changelog is empty, skip
|
||||
if [ -z "$content" ]; then
|
||||
echo "❌ Changelog is empty"
|
||||
return
|
||||
fi
|
||||
# Remove /changelog.md from the path and add /version
|
||||
version_file_path="${path%/changelog.md}/version"
|
||||
# Get version content
|
||||
version_body=$(get_file_content "$version_file_path")
|
||||
# Build the changelog section
|
||||
CHANGELOG_BODY+=$'\n'"<Update label=\"$label\" description=\"$version_body\">"$'\n'"$content"$'\n\n'"</Update>"
|
||||
# Clear the changelog file after processing
|
||||
printf '' > "$path"
|
||||
# Track this file for git commit
|
||||
CLEANED_CHANGELOG_FILES+=("$path")
|
||||
fi
|
||||
}
|
||||
|
||||
# HTTP changelog
|
||||
append_section "Bifrost(HTTP)" transports/changelog.md
|
||||
|
||||
# Core changelog
|
||||
append_section "Core" core/changelog.md
|
||||
|
||||
# Framework changelog
|
||||
append_section "Framework" framework/changelog.md
|
||||
|
||||
# Plugins changelogs
|
||||
for plugin in plugins/*; do
|
||||
name=$(basename "$plugin")
|
||||
append_section "$name" "$plugin/changelog.md"
|
||||
done
|
||||
|
||||
# Write to file
|
||||
mkdir -p docs/changelogs
|
||||
echo "$CHANGELOG_BODY" > docs/changelogs/$VERSION.mdx
|
||||
|
||||
# Update docs.json to include this new changelog route in the Changelogs tab pages array
|
||||
# Uses month-based grouping: current month at top level, older months in groups
|
||||
# Automatically reorganizes when month changes
|
||||
route="changelogs/$VERSION"
|
||||
if ! grep -q "\"$route\"" docs/docs.json; then
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const docs = JSON.parse(fs.readFileSync('docs/docs.json', 'utf8'));
|
||||
|
||||
// Semantic version comparison function
|
||||
// Extracts version from route/filename and compares in descending order (newest first)
|
||||
function compareVersionsDesc(a, b) {
|
||||
// Extract route string from string or object
|
||||
const routeA = typeof a === 'string' ? a : '';
|
||||
const routeB = typeof b === 'string' ? b : '';
|
||||
|
||||
// Extract version from route (e.g., 'changelogs/v1.3.34' -> 'v1.3.34')
|
||||
const versionA = routeA.split('/').pop() || '';
|
||||
const versionB = routeB.split('/').pop() || '';
|
||||
|
||||
// Remove 'v' prefix and split into parts
|
||||
const partsA = versionA.replace(/^v/, '').split(/[.-]/).map(p => {
|
||||
const num = parseInt(p, 10);
|
||||
return isNaN(num) ? p : num;
|
||||
});
|
||||
const partsB = versionB.replace(/^v/, '').split(/[.-]/).map(p => {
|
||||
const num = parseInt(p, 10);
|
||||
return isNaN(num) ? p : num;
|
||||
});
|
||||
|
||||
// Compare each part (major, minor, patch, pre-release, etc.)
|
||||
const maxLength = Math.max(partsA.length, partsB.length);
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
// Release vs prerelease: release is newer (no suffix > has suffix)
|
||||
if (partsA[i] === undefined && partsB[i] !== undefined) {
|
||||
return -1; // A (release) comes first in descending order
|
||||
}
|
||||
if (partsB[i] === undefined && partsA[i] !== undefined) {
|
||||
return 1; // B (release) comes first in descending order
|
||||
}
|
||||
|
||||
const partA = partsA[i];
|
||||
const partB = partsB[i];
|
||||
|
||||
// If both are numbers, compare numerically
|
||||
if (typeof partA === 'number' && typeof partB === 'number') {
|
||||
if (partA !== partB) {
|
||||
return partB - partA; // Descending order
|
||||
}
|
||||
} else {
|
||||
// Handle prerelease strings with numeric suffixes (e.g., 'prerelease10')
|
||||
const strA = String(partA);
|
||||
const strB = String(partB);
|
||||
const matchA = strA.match(/^([a-zA-Z]+)(\\d+)$/);
|
||||
const matchB = strB.match(/^([a-zA-Z]+)(\\d+)$/);
|
||||
|
||||
if (matchA && matchB && matchA[1] === matchB[1]) {
|
||||
// Same prefix, compare numbers numerically
|
||||
const numA = parseInt(matchA[2], 10);
|
||||
const numB = parseInt(matchB[2], 10);
|
||||
if (numA !== numB) {
|
||||
return numB - numA; // Descending order
|
||||
}
|
||||
} else if (strA !== strB) {
|
||||
return strB.localeCompare(strA); // Descending order
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // Equal
|
||||
}
|
||||
|
||||
// Sort a pages array by semver (descending)
|
||||
function sortPagesBySemver(pages) {
|
||||
return pages.slice().sort(compareVersionsDesc);
|
||||
}
|
||||
|
||||
// Get current month/year
|
||||
const releaseDate = new Date('$CURRENT_DATE');
|
||||
const currentDate = new Date();
|
||||
const releaseMonthYear = releaseDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long' });
|
||||
const currentMonthYear = currentDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long' });
|
||||
|
||||
// Find the Changelogs tab
|
||||
const changelogsTab = docs.navigation.tabs.find(tab => tab.tab === 'Changelogs');
|
||||
if (!changelogsTab) {
|
||||
console.error('Changelogs tab not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Find the Open Source menu item
|
||||
const openSourceItem = changelogsTab.menu?.find(item => item.item === 'Open Source');
|
||||
if (!openSourceItem) {
|
||||
console.error('Open Source menu item not found in Changelogs tab');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get all top-level entries and existing groups
|
||||
const topLevelEntries = openSourceItem.pages.filter(p => typeof p === 'string');
|
||||
const existingGroups = openSourceItem.pages.filter(p => typeof p === 'object');
|
||||
|
||||
// Check if we need to group existing top-level entries
|
||||
if (topLevelEntries.length > 0) {
|
||||
// Get the month of the first top-level entry (they should all be from same month)
|
||||
const firstEntryPath = topLevelEntries[0].replace('changelogs/', '') + '.mdx';
|
||||
const firstEntryFile = 'docs/changelogs/' + firstEntryPath;
|
||||
|
||||
let topLevelMonth = null;
|
||||
try {
|
||||
const content = fs.readFileSync(firstEntryFile, 'utf8');
|
||||
const descMatch = content.match(/description:\\s*\"[^\"]*?(\\d{4}-\\d{2}-\\d{2})[^\"]*\"/);
|
||||
if (descMatch) {
|
||||
const entryDate = new Date(descMatch[1]);
|
||||
topLevelMonth = entryDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long' });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(\`Warning: Could not read entry file \${firstEntryFile}: \${e.message}\`);
|
||||
}
|
||||
|
||||
// Only group if the month has changed
|
||||
if (topLevelMonth && topLevelMonth !== releaseMonthYear) {
|
||||
console.log(\`📦 Month changed from \${topLevelMonth} to \${releaseMonthYear}\`);
|
||||
console.log(\`📦 Grouping \${topLevelEntries.length} top-level entries into \${topLevelMonth} group...\`);
|
||||
|
||||
// Create a group for all existing top-level entries
|
||||
const previousMonthGroup = {
|
||||
group: topLevelMonth,
|
||||
pages: sortPagesBySemver(topLevelEntries)
|
||||
};
|
||||
|
||||
// Add this group at the top of existing groups
|
||||
existingGroups.unshift(previousMonthGroup);
|
||||
console.log(\`✅ Created \${topLevelMonth} group with \${topLevelEntries.length} entries (sorted)\`);
|
||||
|
||||
// Clear top-level entries (they're now in the group)
|
||||
openSourceItem.pages = existingGroups;
|
||||
} else {
|
||||
console.log(\`📋 Same month (\${releaseMonthYear}), keeping existing top-level entries\`);
|
||||
// Keep existing structure (top-level entries + groups)
|
||||
openSourceItem.pages = [...topLevelEntries, ...existingGroups];
|
||||
}
|
||||
}
|
||||
|
||||
const newRoute = '$route';
|
||||
|
||||
// Add the new changelog at the top level
|
||||
openSourceItem.pages.unshift(newRoute);
|
||||
console.log(\`✅ Added \${newRoute} to top level\`);
|
||||
|
||||
// Sort the top-level pages array by semver
|
||||
const topLevelPages = openSourceItem.pages.filter(p => typeof p === 'string');
|
||||
const groupPages = openSourceItem.pages.filter(p => typeof p === 'object');
|
||||
|
||||
if (topLevelPages.length > 0) {
|
||||
const sortedTopLevel = sortPagesBySemver(topLevelPages);
|
||||
openSourceItem.pages = [...sortedTopLevel, ...groupPages];
|
||||
console.log(\`✅ Sorted \${topLevelPages.length} top-level pages by semver\`);
|
||||
}
|
||||
|
||||
// Sort each group's pages by semver
|
||||
for (const group of groupPages) {
|
||||
if (group.pages && Array.isArray(group.pages)) {
|
||||
group.pages = sortPagesBySemver(group.pages);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('docs/docs.json', JSON.stringify(docs, null, 2) + '\n');
|
||||
console.log('✅ Updated docs.json');
|
||||
"
|
||||
fi
|
||||
|
||||
# Pulling again before committing
|
||||
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
|
||||
# In detached HEAD state (common in CI), use GITHUB_REF_NAME or default to main
|
||||
CURRENT_BRANCH="${GITHUB_REF_NAME:-main}"
|
||||
fi
|
||||
|
||||
echo "Pulling latest changes from origin/$CURRENT_BRANCH..."
|
||||
if ! git pull origin "$CURRENT_BRANCH"; then
|
||||
echo "❌ Error: git pull origin $CURRENT_BRANCH failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Commit and push changes
|
||||
git add docs/changelogs/$VERSION.mdx
|
||||
git add docs/docs.json
|
||||
# Add all cleaned changelog files
|
||||
for file in "${CLEANED_CHANGELOG_FILES[@]}"; do
|
||||
git add "$file"
|
||||
done
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git commit -m "Adds changelog for $VERSION --skip-ci"
|
||||
git push origin "$CURRENT_BRANCH"
|
||||
Reference in New Issue
Block a user