Android Tutorial [AI] How to generate an automated changelog for a B4A.b4xlib

Hi Fam!

So i jumped into the AI wagon. As Im currently creating the B4XDaisy UIKit, i had an idea last week about creating automated changelogs about this beautiful TailwindCSS based framework.

My Release folder is structured in such a way that each version has its own folder.

1773819090190.png


The purpose of my changelog is to compare 2 b4xlibs and give me whats new and what has changed. So I wrote a prompt so that the AI agent can do that and generate for me a report. It fires up a Powershell script (which I approve) before it runs.


This mermaid chart has been generated with AI from "create for me a mermaid chart based on my xxxx script". As I also wanted to get it as an image, I included that it should help me with a png to print it. So it generated an HTML file with a button to click and download the image.

whatsnew-process-flowchart.png


Here is my powershell script generated by AI.


Usage.... "whatsnew 0.50 0.40"

B4X:
Param(
    [Parameter(Mandatory=$false, Position=0)]
    [string]$OldVersion,
    [Parameter(Mandatory=$false, Position=1)]
    [string]$OutReleaseVersion,
    [Parameter(Mandatory=$false, Position=2)]
    [string]$NewLibPath
)
if (-not $OldVersion) {
    Write-Host "Usage: .\whatsnew.ps1 <old-version> [out-release-version] [new-b4xlib-path]  e.g. .\whatsnew.ps1 0.40 0.50 C:\dist\Libraries\B4XDaisyUIKit.b4xlib"
    exit 1
}
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$releasesLibDir = Join-Path $scriptDir "Releases\v$OldVersion\Libraries"
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction SilentlyContinue
# locate old library (if present)
$oldLib = $null
if (Test-Path $releasesLibDir) {
    $oldLib = Get-ChildItem -Path $releasesLibDir -Filter "*.b4xlib" -File | Where-Object { $_.Name -match 'Daisy' } | Select-Object -First 1
    if (-not $oldLib) { $oldLib = Get-ChildItem -Path $releasesLibDir -Filter "*.b4xlib" -File | Select-Object -First 1 }
}
function Get-ZipBasMap([string]$zipPath) {
    $map = @{}
    try {
        $z = [System.IO.Compression.ZipFile]::OpenRead($zipPath)
    } catch {
        Write-Error ("Failed to open {0}: {1}" -f $zipPath, $_)
        return $map
    }
    foreach ($entry in $z.Entries) {
        if ($entry.FullName -like '*.bas') {
            $stream = $entry.Open()
            $sha = [System.Security.Cryptography.SHA256]::Create()
            try {
                $hashBytes = $sha.ComputeHash($stream)
                $hash = [System.BitConverter]::ToString($hashBytes) -replace '-', ''
                $map[$entry.Name] = $hash
            } finally {
                $stream.Close()
                $sha.Dispose()
            }
        }
    }
    $z.Dispose()
    return $map
}
$oldMap = @{}
if ($oldLib) { $oldMap = Get-ZipBasMap $oldLib.FullName }
# resolve new lib path
$newLibPathResolved = $null
if ($NewLibPath) { $newLibPathResolved = $NewLibPath }
else {
    $candidate1 = Join-Path $scriptDir 'dist\Libraries\B4XDaisyUIKit.b4xlib'
    $candidate2 = 'C:\b4a\libraries\B4XDaisyUIKit.b4xlib'
    if (Test-Path $candidate1) { $newLibPathResolved = $candidate1 }
    elseif (Test-Path $candidate2) { $newLibPathResolved = $candidate2 }
}
$newMap = @{}
if ($newLibPathResolved) { $newMap = Get-ZipBasMap $newLibPathResolved }
# Compare libs if possible
$whatsnew = @()
$modified = @()
$removed = @()
if ($newMap.Count -gt 0) {
    if ($oldMap.Count -eq 0) {
        # everything in new is new
        $whatsnew = $newMap.Keys | Sort-Object
    } else {
        $oldKeys = $oldMap.Keys
        $newKeys = $newMap.Keys
        foreach ($k in $newKeys) {
            if (-not ($oldKeys -contains $k)) { $whatsnew += $k }
            else { if ($oldMap[$k] -ne $newMap[$k]) { $modified += $k } }
        }
        foreach ($k in $oldKeys) { if (-not ($newKeys -contains $k)) { $removed += $k } }
    }
    # write basic whatsnewlist (new files only)
    # Disabled: Only generating markdown changelog
    if ($whatsnew.Count -eq 0) {
        Write-Host "No new B4XDaisy*.bas files found comparing libs (old: v$OldVersion)."
    } else {
        Write-Host "Found $($whatsnew.Count) new file(s):"
        $whatsnew | ForEach-Object { Write-Host "  - $_" }
    }
    # write detailed whatsnew into release root if requested
    # Disabled: Only generating markdown changelog
    if (-not [string]::IsNullOrWhiteSpace($OutReleaseVersion)) {
        # Skip whatsnew.txt generation - only create markdown changelog
    }
    # Generate diffs for new and modified files to aid release notes
    # Diff generation disabled by user request. No per-file diffs will be produced.
        # Generate a markdown CHANGELOG in the release root (no links to diffs)
        if (-not [string]::IsNullOrWhiteSpace($OutReleaseVersion)) {
            $releaseRoot = Join-Path $scriptDir "Releases\v$OutReleaseVersion"
            $changelogPath = Join-Path $releaseRoot ("CHANGELOG v{0}.md" -f $OutReleaseVersion)
            $repoUrl = 'https://github.com/Mashiane/Sithaso-B4XDaisy-UIKit---Native-Android-Components-inspired-by-DaisyUI'
            $chLines = @()
            $chLines += "# Changelog - v$OutReleaseVersion"
            $chLines += ""
            $chLines += ("Generated on: " + (Get-Date -Format 'yyyy-MM-dd'))
            $chLines += ""
            $chLines += ("Compare: v$OldVersion -> v$OutReleaseVersion")
            $chLines += ""
            $chLines += "Repository: $repoUrl"
            $chLines += ""
            $chLines += "## Summary"
            $chLines += ""
            # Sort new files alphabetically; sort modified files alphabetically
            $newSorted = $whatsnew | Sort-Object
            $modSorted = $modified | Sort-Object
            $chLines += ("- New components: " + ($newSorted.Count))
            $chLines += ("- Modified components: " + ($modSorted.Count))
            $chLines += ("- Removed components: " + ($removed.Count))
            $chLines += ""
            $chLines += "## New Files"
            $chLines += ""
            if ($newSorted.Count -eq 0) { $chLines += "- (none)" } else { $newSorted | ForEach-Object { $chLines += ("- " + $_) } }
            $chLines += ""
            $chLines += "## Modified Files"
            $chLines += ""
            if ($modSorted.Count -eq 0) { $chLines += "- (none)" } else { $modSorted | ForEach-Object { $chLines += ("- " + $_) } }
            $chLines += ""
            $chLines += "## Removed Files"
            $chLines += ""
            if ($removed.Count -eq 0) { $chLines += "- (none)" } else { $removed | ForEach-Object { $chLines += ("- " + $_) } }
            $chLines | Out-File -FilePath $changelogPath -Encoding UTF8
            Write-Host "Wrote release changelog to $changelogPath"
        }
        exit 0
}
# Fallback: legacy behavior comparing local source files to old lib entries
$localDir = Join-Path $scriptDir 'B4A'
if (-not (Test-Path $localDir)) { Write-Error "Local B4A folder not found at $localDir"; exit 5 }
$localFiles = Get-ChildItem -Path $localDir -Recurse -Filter 'B4XDaisy*.bas' -File | ForEach-Object { $_.Name } | Sort-Object -Unique
$zipNames = $oldMap.Keys
$legacyNew = @()
foreach ($f in $localFiles) { if (-not ($zipNames -contains $f)) { $legacyNew += $f } }
# Disabled: Only generating markdown changelog
if ($legacyNew.Count -eq 0) {
    Write-Host "No new B4XDaisy*.bas files found for version $OldVersion."
} else {
    Write-Host "Found $($legacyNew.Count) new file(s):"
    $legacyNew | ForEach-Object { Write-Host "  - $_" }
}
# Skip whatsnewlist generation - only create markdown changelog
if (-not [string]::IsNullOrWhiteSpace($OutReleaseVersion)) {
    # Skip whatsnew.txt generation - only create markdown changelog
}
exit 0

The Generated Changelog...

1773820334674.png


I did not want a detailed "diff" per file...


#SharingTheGoodness
 
Top