Skip to content

Commit

Permalink
used changelog.js from ci_cd to automate verison generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Bioblaze committed Dec 19, 2024
1 parent e46c6ca commit 6623a8b
Show file tree
Hide file tree
Showing 2 changed files with 305 additions and 41 deletions.
116 changes: 75 additions & 41 deletions .github/workflows/build-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,74 @@ on:
workflow_dispatch:

jobs:
get-latest-sha:
name: 🔍 Get Latest SHA & Base Version
runs-on: ubuntu-latest
outputs:
version_string: ${{ steps.new_version.outputs.version_string }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: blazium-engine/vscode-anko-highlighter
ref: master
fetch-depth: 2

- name: Get Latest Commit SHA
id: get_sha
run: echo "sha=$(git rev-parse HEAD)" >> $ENV:GITHUB_OUTPUT

- name: Generate Changelog.json
id: new_version
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_OWNER: blazium-engine
GITHUB_REPO: vscode-anko-highlighter
BASE_BRANCH: 0acc2206c2a57f104d3295ec4e5b5ce7b7d9d4dd
CURRENT_BRANCH: ${{ steps.get_sha.outputs.sha }}
MAJOR_VERSION: 0
MINOR_VERSION: 0
PATCH_VERSION: 2
run: |
# Ensure the working directory is correct
Set-Location -Path "${{ github.workspace }}"
# Trigger the changelog generation script
node ./scripts/changelog.js $(Get-Location)
# Validate if changelog.json exists
if (-Not (Test-Path -Path "changelog.json")) {
Write-Error "changelog.json not found."
exit 1
}
# Parse changelog.json to extract version information
$changelog = Get-Content -Raw -Path "changelog.json" | ConvertFrom-Json
$major = $changelog.version.major
$minor = $changelog.version.minor
$patch = $changelog.version.patch
# Combine version variables into a single variable
$version = "$major.$minor.$patch"
# Set outputs
echo "version=$version" >> $ENV:GITHUB_OUTPUT
# Create a debug summary
$summary = @(
"# Version Information",
"- Major: $major",
"- Minor: $minor",
"- Patch: $patch",
"- Full Version: $version"
) -join "`n"
echo $summary >> $ENV:GITHUB_STEP_SUMMARY
build:
name: Building VSIX Package
needs: get-latest-sha
runs-on: windows-latest
outputs:
version: ${{ steps.validate.outputs.newVersion }}
Expand All @@ -27,54 +94,21 @@ jobs:
- name: Install dependencies
run: |
cd .\anko\
npm install
- name: Determine Version Bump
id: version
run: |
$comment = "${{ github.event.head_commit.message }}"
if ($comment -match "#major") {
echo "bumpType=major" >> $ENV:GITHUB_OUTPUT
} elseif ($comment -match "#minor") {
echo "bumpType=minor" >> $ENV:GITHUB_OUTPUT
} else {
echo "bumpType=patch" >> $ENV:GITHUB_OUTPUT
}
- name: Execute Update Script
id: update
run: |
$DebugInfo = @()
$DebugInfo += "Executing update.ps1 with VersionType=${{ steps.version.outputs.bumpType }}"
$result = pwsh ./scripts/update.ps1 ${{ steps.version.outputs.bumpType }}
echo "version=$result" >> $ENV:GITHUB_OUTPUT
echo "$DebugInfo" >> $ENV:GITHUB_STEP_SUMMARY
shell: pwsh

- name: Validate Version Output
id: validate
run: |
$version = "${{ steps.update.outputs.version }}"
echo "Validating version: $version" >> $ENV:GITHUB_STEP_SUMMARY
if ($version -notmatch "^\d+\.\d+\.\d+$") {
Write-Error "Invalid version format: $version"
exit 1
}
echo "newVersion=$version" >> $ENV:GITHUB_OUTPUT
npm Install
- name: Build VSIX file
run: |
cd .\anko\
vsce package --allow-missing-repository --out ..\dist\blazium-anko-extension-${{ steps.validate.outputs.newVersion }}.vsix
vsce package --allow-missing-repository --out ..\dist\blazium-anko-extension-${{ needs.get-latest-sha.outputs.version_string }}.vsix
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: blazium-anko-extension-vsix
path: dist/blazium-anko-extension-${{ steps.validate.outputs.newVersion }}.vsix
path: dist/blazium-anko-extension-${{ needs.get-latest-sha.outputs.version_string }}.vsix

release:
needs: build
needs: [ build, get-latest-sha ]
runs-on: windows-latest
permissions:
contents: write # Grants write permissions for content operations
Expand All @@ -91,8 +125,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.build.outputs.version }}
release_name: Blazium Anko Extension v${{ needs.build.outputs.version }}
tag_name: ${{ needs.get-latest-sha.outputs.version_string }}
release_name: Blazium Anko Extension v${{ needs.get-latest-sha.outputs.version_string }}
draft: false
prerelease: false

Expand All @@ -102,8 +136,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/dist/blazium-anko-extension-${{ needs.build.outputs.version }}.vsix
asset_name: blazium-anko-extension-${{ needs.build.outputs.version }}.vsix
asset_path: ${{ github.workspace }}/dist/blazium-anko-extension-${{ needs.get-latest-sha.outputs.version_string }}.vsix
asset_name: blazium-anko-extension-${{ needs.get-latest-sha.outputs.version_string }}.vsix
asset_content_type: application/octet-stream


230 changes: 230 additions & 0 deletions scripts/changelog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
const https = require('https');
const fs = require('fs');
const path = require('path');

// Load environment variables from .env file
const token = process.env.GITHUB_TOKEN;
const owner = process.env.GITHUB_OWNER; // The owner of the repository
const repo = process.env.GITHUB_REPO; // The repository name
const baseBranch = process.env.BASE_BRANCH; // The base branch
const currentBranch = process.env.CURRENT_BRANCH; // The current branch


var version = {
major: process.env.MAJOR_VERSION || 0,
minor: process.env.MINOR_VERSION || 1,
patch: process.env.PATCH_VERSION || 0
}


// API base URL for the repository
const apiBaseUrl = `https://api.github.com/repos/${owner}/${repo}`;

function httpsGet(url, headers = {}) {
return new Promise((resolve, reject) => {
const options = {
method: 'GET',
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'blazium-engine/blazium ci/cd v0.0.1 prototype',
...headers,
},
};

const req = https.request(url, options, (res) => {
let data = '';

res.on('data', (chunk) => {
data += chunk;
});

res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(data));
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
});
});

req.on('error', (err) => {
reject(err);
});

req.end();
});
}

// Function to compare two branches and get commits
async function compareBranches(baseBranch, currentBranch) {
const url = `${apiBaseUrl}/compare/${baseBranch}...${currentBranch}`;
try {
const data = await httpsGet(url);
return data; // Returns the comparison data (commits, files, etc.)
} catch (error) {
console.error(`Error comparing branches ${baseBranch} and ${currentBranch}:`, error.message);
return null;
}
}

// Function to get the files changed in a specific commit
async function getCommitFiles(commitSha) {
const url = `${apiBaseUrl}/commits/${commitSha}`;
try {
const data = await httpsGet(url);
return data.files; // Returns the list of files changed in the commit
} catch (error) {
console.error(`Error fetching files for commit ${commitSha}:`, error.message);
return [];
}
}
// Function to generate a changelog and collect stats from the comparison data
async function generateChangelog(baseBranch, currentBranch, outputDir = __dirname) {
const comparisonData = await compareBranches(baseBranch, currentBranch);

if (!comparisonData) {
return;
}

const commits = comparisonData.commits;
const filesChanged = new Set(); // To store unique file paths
let totalPRs = 0;
const changelog = [];
const contributorStats = {};
let firstChangeDate = null;
let lastChangeDate = null;

// Loop through commits to gather stats and changelog
for (const commit of commits) {
const commitSha = commit.sha;
const commitMessage = commit.commit.message;
const regex = /#(skip)/ig;
const matches = commitMessage.match(regex);

if (matches) {
continue;
}
const commitDate = new Date(commit.commit.committer.date);
const commitUser = commit.committer.login; // The username of the person who made the commit
const prNumber = extractPRNumberFromCommitMessage(commitMessage);
const semVerLabel = getSemVerLabel(commitMessage);

// Get files changed in the commit
const commitFiles = await getCommitFiles(commitSha);
commitFiles.forEach(file => filesChanged.add(file.filename));

// Update first and last change dates
if (!firstChangeDate || commitDate < firstChangeDate) {
firstChangeDate = commitDate;
}
if (!lastChangeDate || commitDate > lastChangeDate) {
lastChangeDate = commitDate;
}

// Add user contribution count
if (!contributorStats[commitUser]) {
contributorStats[commitUser] = {
count: 0,
commits: []
};
}
contributorStats[commitUser].count += 1;
contributorStats[commitUser].commits.push(commitSha);

// Add to changelog
changelog.push({
sha: commitSha,
message: commitMessage,
date: commitDate,
user: commitUser,
pr: prNumber ? `PR #${prNumber}` : null,
label: semVerLabel
});

// Count PRs
if (prNumber) {
totalPRs++;
}

if (semVerLabel === "major") {
version.major++;
version.minor = 0;
version.patch = 0;
} else if (semVerLabel === "minor") {
version.minor++;
version.patch = 0;
} else if (!prNumber) {
version.patch++;
}
}

// Calculate time differences
const timeSinceFirstChange = firstChangeDate ? (Date.now() - firstChangeDate.getTime()) : null;
const timeSinceLastChange = lastChangeDate ? (Date.now() - lastChangeDate.getTime()) : null;

// Prepare a list of unique users and their contribution counts
const uniqueContributors = Object.keys(contributorStats).map(user => ({
username: user,
contributions: contributorStats[user].count
}));

// Prepare the result object
const result = {
baseBranch,
version,
currentBranch,
totalCommits: commits.length,
totalPRs,
totalFilesChanged: filesChanged.size,
timeSinceFirstChange: timeSinceFirstChange ? `${Math.floor(timeSinceFirstChange / (1000 * 60 * 60 * 24))} days` : null,
timeSinceLastChange: timeSinceLastChange ? `${Math.floor(timeSinceLastChange / (1000 * 60 * 60 * 24))} days` : null,
totalContributors: uniqueContributors.length,
uniqueContributors,
changelog
};

// Save the result
saveToFile(result, `changelog_${baseBranch}_to_${currentBranch}.json`, outputDir);
saveToFile(result, `changelog.json`, outputDir);
}

function saveToFile(data, fileName, outputDir) {
const filePath = path.join(outputDir, fileName);
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
console.log(`Changelog and stats exported to ${filePath}`);
}

// Function to extract PR numbers from a commit message (e.g., "Merge pull request #XYZ")
function extractPRNumberFromCommitMessage(commitMessage) {
const prRegex = /Merge pull request #(\d+)/;
const match = commitMessage.match(prRegex);
return match ? parseInt(match[1], 10) : null;
}

function getSemVerLabel(message) {
const regex = /#(major|minor|patch)/ig;
const matches = message.match(regex);

if (matches) {
const uniqueMatches = [...new Set(matches.map(match => match.toLowerCase()))];

// If only one valid label is present, return it (without the '#').
if (uniqueMatches.length === 1) {
return uniqueMatches[0].replace('#', '');
}

// If there are multiple valid labels, take the last one.
return uniqueMatches[uniqueMatches.length - 1].replace('#', '');
}

// Default to 'patch' if no valid labels are found.
return 'patch';
}

// Main function
(async function main() {
const args = process.argv.slice(2);
const outputDir = args[0] || __dirname;
await generateChangelog(baseBranch, currentBranch, outputDir);
})();

0 comments on commit 6623a8b

Please sign in to comment.