mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
🚀 Enhance Release Notes Generation with Previous Tag Detection (#394)
* feat: add automated release notes generation with previous tag detection * chore: npm run format
This commit is contained in:
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -84,6 +84,11 @@ jobs:
|
|||||||
echo "RELEASE_TAG=$(echo $VERSION_JSON | jq -r .releaseTag)" >> $GITHUB_OUTPUT
|
echo "RELEASE_TAG=$(echo $VERSION_JSON | jq -r .releaseTag)" >> $GITHUB_OUTPUT
|
||||||
echo "RELEASE_VERSION=$(echo $VERSION_JSON | jq -r .releaseVersion)" >> $GITHUB_OUTPUT
|
echo "RELEASE_VERSION=$(echo $VERSION_JSON | jq -r .releaseVersion)" >> $GITHUB_OUTPUT
|
||||||
echo "NPM_TAG=$(echo $VERSION_JSON | jq -r .npmTag)" >> $GITHUB_OUTPUT
|
echo "NPM_TAG=$(echo $VERSION_JSON | jq -r .npmTag)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Get the previous tag for release notes generation
|
||||||
|
CURRENT_TAG=$(echo $VERSION_JSON | jq -r .releaseTag)
|
||||||
|
PREVIOUS_TAG=$(node scripts/get-previous-tag.js "$CURRENT_TAG" || echo "")
|
||||||
|
echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT
|
||||||
env:
|
env:
|
||||||
IS_NIGHTLY: ${{ steps.vars.outputs.is_nightly }}
|
IS_NIGHTLY: ${{ steps.vars.outputs.is_nightly }}
|
||||||
MANUAL_VERSION: ${{ inputs.version }}
|
MANUAL_VERSION: ${{ inputs.version }}
|
||||||
@@ -158,11 +163,20 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||||
run: |
|
run: |
|
||||||
gh release create ${{ steps.version.outputs.RELEASE_TAG }} \
|
# Build the gh release create command with appropriate options
|
||||||
bundle/gemini.js \
|
RELEASE_CMD="gh release create ${{ steps.version.outputs.RELEASE_TAG }} bundle/gemini.js --target \"$RELEASE_BRANCH\" --title \"Release ${{ steps.version.outputs.RELEASE_TAG }}\""
|
||||||
--target "$RELEASE_BRANCH" \
|
|
||||||
--title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \
|
# Add previous tag for release notes if available
|
||||||
--generate-notes
|
if [[ -n "${{ steps.version.outputs.PREVIOUS_TAG }}" ]]; then
|
||||||
|
echo "Generating release notes from previous tag: ${{ steps.version.outputs.PREVIOUS_TAG }}"
|
||||||
|
RELEASE_CMD="$RELEASE_CMD --generate-notes --notes-start-tag ${{ steps.version.outputs.PREVIOUS_TAG }}"
|
||||||
|
else
|
||||||
|
echo "No previous tag found, generating release notes from repository history"
|
||||||
|
RELEASE_CMD="$RELEASE_CMD --generate-notes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute the release command
|
||||||
|
eval $RELEASE_CMD
|
||||||
|
|
||||||
- name: Create Issue on Failure
|
- name: Create Issue on Failure
|
||||||
if: failure()
|
if: failure()
|
||||||
|
|||||||
213
scripts/get-previous-tag.js
Normal file
213
scripts/get-previous-tag.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the correct previous tag for release notes generation.
|
||||||
|
* This function handles the complexity of mixed tag types (regular releases vs nightly releases).
|
||||||
|
*
|
||||||
|
* @param {string} currentTag - The current release tag (e.g., "v0.1.23")
|
||||||
|
* @returns {string|null} - The previous tag to compare against, or null if no suitable tag found
|
||||||
|
*/
|
||||||
|
export function getPreviousTag(currentTag) {
|
||||||
|
try {
|
||||||
|
// Parse the current tag to understand its type
|
||||||
|
const currentTagInfo = parseTag(currentTag);
|
||||||
|
if (!currentTagInfo) {
|
||||||
|
console.error(`Invalid current tag format: ${currentTag}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the appropriate previous tag based on the current tag type
|
||||||
|
let previousTag = null;
|
||||||
|
|
||||||
|
if (currentTagInfo.isNightly) {
|
||||||
|
// For nightly releases, find the last stable release
|
||||||
|
previousTag = findLastStableTag(currentTagInfo);
|
||||||
|
} else {
|
||||||
|
// For stable releases, find the previous stable release
|
||||||
|
previousTag = findPreviousStableTag(currentTagInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return previousTag;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting previous tag:', error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a tag string to extract version information and type
|
||||||
|
*/
|
||||||
|
function parseTag(tag) {
|
||||||
|
// Remove 'v' prefix if present
|
||||||
|
const cleanTag = tag.startsWith('v') ? tag.substring(1) : tag;
|
||||||
|
|
||||||
|
// Match pattern: X.Y.Z or X.Y.Z-prerelease
|
||||||
|
const match = cleanTag.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, major, minor, patch, prerelease] = match;
|
||||||
|
|
||||||
|
return {
|
||||||
|
original: tag,
|
||||||
|
major: parseInt(major),
|
||||||
|
minor: parseInt(minor),
|
||||||
|
patch: parseInt(patch),
|
||||||
|
prerelease: prerelease || null,
|
||||||
|
isNightly: prerelease && prerelease.startsWith('nightly'),
|
||||||
|
isPreview: prerelease && prerelease.startsWith('preview'),
|
||||||
|
version: `${major}.${minor}.${patch}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the last stable tag for a nightly release
|
||||||
|
* Assumes version numbers are incremental and checks backwards from current version
|
||||||
|
*/
|
||||||
|
function findLastStableTag(currentTagInfo) {
|
||||||
|
// For nightly releases, find the stable version of the same version number first
|
||||||
|
const baseVersion = `v${currentTagInfo.version}`;
|
||||||
|
|
||||||
|
// Check if the stable version of the current version exists
|
||||||
|
if (tagExists(baseVersion)) {
|
||||||
|
return baseVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not, look for the previous stable versions by decrementing version numbers
|
||||||
|
let { major, minor, patch } = currentTagInfo;
|
||||||
|
|
||||||
|
// Try decrementing patch version first
|
||||||
|
while (patch > 0) {
|
||||||
|
patch--;
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try decrementing minor version
|
||||||
|
while (minor > 0) {
|
||||||
|
minor--;
|
||||||
|
patch = 999; // Start from a high patch number and work backwards
|
||||||
|
while (patch >= 0) {
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
patch--;
|
||||||
|
// Don't check too many patch versions to avoid infinite loops
|
||||||
|
if (patch < 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try decrementing major version
|
||||||
|
while (major > 0) {
|
||||||
|
major--;
|
||||||
|
minor = 999; // Start from a high minor number and work backwards
|
||||||
|
while (minor >= 0) {
|
||||||
|
patch = 999;
|
||||||
|
while (patch >= 0) {
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
patch--;
|
||||||
|
if (patch < 0) break;
|
||||||
|
}
|
||||||
|
minor--;
|
||||||
|
if (minor < 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the previous stable tag for a stable release
|
||||||
|
* Assumes version numbers are incremental and checks backwards from current version
|
||||||
|
*/
|
||||||
|
function findPreviousStableTag(currentTagInfo) {
|
||||||
|
let { major, minor, patch } = currentTagInfo;
|
||||||
|
|
||||||
|
// Try decrementing patch version first
|
||||||
|
while (patch > 0) {
|
||||||
|
patch--;
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try decrementing minor version
|
||||||
|
while (minor > 0) {
|
||||||
|
minor--;
|
||||||
|
patch = 999; // Start from a high patch number and work backwards
|
||||||
|
while (patch >= 0) {
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
patch--;
|
||||||
|
// Don't check too many patch versions to avoid infinite loops
|
||||||
|
if (patch < 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try decrementing major version
|
||||||
|
while (major > 0) {
|
||||||
|
major--;
|
||||||
|
minor = 999; // Start from a high minor number and work backwards
|
||||||
|
while (minor >= 0) {
|
||||||
|
patch = 999;
|
||||||
|
while (patch >= 0) {
|
||||||
|
const candidateTag = `v${major}.${minor}.${patch}`;
|
||||||
|
if (tagExists(candidateTag)) {
|
||||||
|
return candidateTag;
|
||||||
|
}
|
||||||
|
patch--;
|
||||||
|
if (patch < 0) break;
|
||||||
|
}
|
||||||
|
minor--;
|
||||||
|
if (minor < 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a git tag exists
|
||||||
|
*/
|
||||||
|
function tagExists(tag) {
|
||||||
|
try {
|
||||||
|
execSync(`git rev-parse --verify ${tag}`, { stdio: 'ignore' });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI usage
|
||||||
|
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
||||||
|
const currentTag = process.argv[2];
|
||||||
|
|
||||||
|
if (!currentTag) {
|
||||||
|
console.error('Usage: node get-previous-tag.js <current-tag>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousTag = getPreviousTag(currentTag);
|
||||||
|
if (previousTag) {
|
||||||
|
console.log(previousTag);
|
||||||
|
} else {
|
||||||
|
console.error('No suitable previous tag found');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user