| #!/usr/bin/env bash |
|
|
| VERSION=$1 |
|
|
| if [ -z "$VERSION" ]; then |
| echo "Usage: $0 <version>" |
| echo "Example: $0 0.10.0" |
| exit 1 |
| fi |
|
|
| VERSION_WITH_PREFIX="cli-v$VERSION" |
|
|
| |
| if [ -f "docs/changelogs/$VERSION_WITH_PREFIX.mdx" ]; then |
| echo "β
Changelog for $VERSION_WITH_PREFIX already exists" |
| exit 0 |
| fi |
|
|
| |
| source "$(dirname "$0")/changelog-utils.sh" |
|
|
| |
| CURRENT_DATE=$(date +"%Y-%m-%d") |
|
|
| |
| CLI_CHANGELOG_PATH="cli/changelog.md" |
| if [ ! -f "$CLI_CHANGELOG_PATH" ]; then |
| echo "β CLI changelog not found at $CLI_CHANGELOG_PATH" |
| exit 1 |
| fi |
|
|
| CHANGELOG_CONTENT=$(get_file_content "$CLI_CHANGELOG_PATH") |
| if [ -z "$CHANGELOG_CONTENT" ]; then |
| echo "β CLI changelog is empty" |
| exit 1 |
| fi |
|
|
| |
| CHANGELOG_BODY="--- |
| title: \"v$VERSION\" |
| description: \"v$VERSION changelog - $CURRENT_DATE\" |
| --- |
| |
| <Update label=\"Bifrost CLI\" description=\"v$VERSION\"> |
| $CHANGELOG_CONTENT |
| |
| </Update> |
| " |
|
|
| |
| mkdir -p docs/changelogs |
| echo "$CHANGELOG_BODY" > "docs/changelogs/$VERSION_WITH_PREFIX.mdx" |
| echo "β
Created docs/changelogs/$VERSION_WITH_PREFIX.mdx" |
|
|
| |
| printf '' > "$CLI_CHANGELOG_PATH" |
| echo "β
Cleared $CLI_CHANGELOG_PATH" |
|
|
| |
| route="changelogs/$VERSION_WITH_PREFIX" |
| 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/cli-v0.10.0' -> 'cli-v0.10.0') |
| const versionA = routeA.split('/').pop() || ''; |
| const versionB = routeB.split('/').pop() || ''; |
| |
| // Remove 'cli-v' or 'v' prefix and split into parts |
| const partsA = versionA.replace(/^(cli-)?v/, '').split(/[.-]/).map(p => { |
| const num = parseInt(p, 10); |
| return isNaN(num) ? p : num; |
| }); |
| const partsB = versionB.replace(/^(cli-)?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 Bifrost CLI menu item |
| const cliMenuItem = changelogsTab.menu.find(item => item.item === 'Bifrost CLI'); |
| if (!cliMenuItem) { |
| console.error('Bifrost CLI menu item not found'); |
| process.exit(1); |
| } |
| |
| // Get all top-level entries and existing groups |
| const topLevelEntries = cliMenuItem.pages.filter(p => typeof p === 'string'); |
| const existingGroups = cliMenuItem.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) |
| cliMenuItem.pages = existingGroups; |
| } else { |
| console.log(\`π Same month (\${releaseMonthYear}), keeping existing top-level entries\`); |
| // Keep existing structure (top-level entries + groups) |
| cliMenuItem.pages = [...topLevelEntries, ...existingGroups]; |
| } |
| } |
| |
| const newRoute = '$route'; |
| |
| // Add the new changelog at the top level |
| cliMenuItem.pages.unshift(newRoute); |
| console.log(\`β
Added \${newRoute} to top level\`); |
| |
| // Sort the top-level pages array by semver |
| const topLevelPages = cliMenuItem.pages.filter(p => typeof p === 'string'); |
| const groupPages = cliMenuItem.pages.filter(p => typeof p === 'object'); |
| |
| if (topLevelPages.length > 0) { |
| const sortedTopLevel = sortPagesBySemver(topLevelPages); |
| cliMenuItem.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 |
|
|
| |
| CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" |
| if [ "$CURRENT_BRANCH" = "HEAD" ]; then |
| |
| 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 |
|
|
| |
| git add "docs/changelogs/$VERSION_WITH_PREFIX.mdx" |
| git add docs/docs.json |
| git add "$CLI_CHANGELOG_PATH" |
| git config user.name "github-actions[bot]" |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" |
| git commit -m "Adds CLI changelog for v$VERSION --skip-pipeline" |
| git push origin "$CURRENT_BRANCH" |
|
|
| echo "β
Pushed CLI changelog for v$VERSION" |
|
|