From a7e3d3dff6253b9cb31134919889329cb0dd0e73 Mon Sep 17 00:00:00 2001 From: Yakir Sitbon Date: Sun, 23 Aug 2026 14:23:57 +0300 Subject: [PATCH 1/5] Internal: Add deploy scripts --- .build-rsync-exclude | 1 + .github/workflows/php-coding-standards.yml | 1 + .github/workflows/phpunit.yml | 1 + .github/workflows/release.yml | 198 +++++++++++++++++++++ package.json | 5 +- scripts/next-version.js | 30 ++++ scripts/sync-version.js | 27 +++ scripts/validate-build-files.sh | 73 ++++++++ scripts/validate-readme-changelog.js | 53 ++++++ 9 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/next-version.js create mode 100644 scripts/sync-version.js create mode 100755 scripts/validate-build-files.sh create mode 100644 scripts/validate-readme-changelog.js diff --git a/.build-rsync-exclude b/.build-rsync-exclude index 7f0ff11..643873b 100644 --- a/.build-rsync-exclude +++ b/.build-rsync-exclude @@ -20,5 +20,6 @@ package.json package-lock.json Gruntfile.js README.md +scripts/ aryo-activity-log/ aryo-activity-log.*.zip diff --git a/.github/workflows/php-coding-standards.yml b/.github/workflows/php-coding-standards.yml index 3eae26c..d3389ef 100644 --- a/.github/workflows/php-coding-standards.yml +++ b/.github/workflows/php-coding-standards.yml @@ -4,6 +4,7 @@ on: push: pull_request: workflow_dispatch: + workflow_call: concurrency: cancel-in-progress: true diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 818496b..b99dd77 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -3,6 +3,7 @@ name: PHPUnit Tests on: push: pull_request: + workflow_call: concurrency: cancel-in-progress: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cbe3f5b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,198 @@ +name: Release + +on: + workflow_dispatch: + inputs: + bump: + description: Version bump + required: true + type: choice + options: [patch, minor, major] + dry_run: + description: Dry run (no git push, no WP.org commit) + required: false + type: boolean + default: false + +concurrency: + group: wordpress-org-release + cancel-in-progress: false + +permissions: + contents: read + +jobs: + phpcs: + if: >- + github.ref == 'refs/heads/master' + && ( github.actor == 'KingYes' || github.actor == 'bainternet' || github.actor == 'arielk' ) + && startsWith( github.repository, 'elementor/' ) + uses: ./.github/workflows/php-coding-standards.yml + + phpunit: + if: >- + github.ref == 'refs/heads/master' + && ( github.actor == 'KingYes' || github.actor == 'bainternet' || github.actor == 'arielk' ) + && startsWith( github.repository, 'elementor/' ) + uses: ./.github/workflows/phpunit.yml + + release: + needs: [phpcs, phpunit] + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + tools: composer:v2 + + - uses: actions/cache@v4 + with: + path: vendor + key: composer-${{ hashFiles('composer.json') }} + restore-keys: | + composer- + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-progress + + - name: Lint + run: composer lint + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install npm dependencies + run: npm install + + - name: Compute next version + run: | + VERSION=$(node scripts/next-version.js ${{ inputs.bump }}) + echo "VERSION=$VERSION" >> "$GITHUB_ENV" + echo "TAG=v$VERSION" >> "$GITHUB_ENV" + echo "Next version: $VERSION" + + - name: Validate readme changelog + run: node scripts/validate-readme-changelog.js "${{ env.VERSION }}" + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Bump version + run: npm version ${{ inputs.bump }} --no-git-tag-version + + - name: Sync version to plugin files + run: npm run version:sync + + - name: Build plugin directory + run: npm run package + + - name: Validate build + env: + PLUGIN_SLUG: aryo-activity-log + PLUGIN_VERSION: ${{ env.VERSION }} + run: bash scripts/validate-build-files.sh + + - name: Create zip + run: | + zip -r aryo-activity-log.${{ env.VERSION }}.zip ./aryo-activity-log/* + + - name: Commit version bump + if: ${{ inputs.dry_run != true }} + run: | + git add package.json package-lock.json aryo-activity-log.php readme.txt + git commit -m "Internal: Release ${{ env.VERSION }}" + + - name: Create tag + if: ${{ inputs.dry_run != true }} + run: git tag "${{ env.TAG }}" + + - name: Generate changelog + id: changelog + env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + run: | + TAG="${{ env.TAG }}" + + if ${{ inputs.dry_run }}; then + BODY="Dry-run release for version ${{ env.VERSION }}." + else + PREV=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "") + if [ -z "$PREV" ]; then + LOG=$(git log --no-merges --format="%H %s" "$TAG") + else + LOG=$(git log --no-merges --format="%H %s" "${PREV}..${TAG}") + fi + BODY=$(echo "$LOG" \ + | grep -v -E ' (bump: v|CI: |Internal: |Revert: )' \ + | while read -r hash msg; do + short=${hash:0:7} + msg=$(echo "$msg" | sed "s|(#\([0-9]*\))|[#\1](${REPO_URL}/pull/\1)|g") + echo "- [\`${short}\`](${REPO_URL}/commit/${hash}) ${msg}" + done) + fi + + { + echo "body<> "$GITHUB_OUTPUT" + + - name: Push commit and tag + if: ${{ inputs.dry_run != true }} + run: | + git push origin master + git push origin "${{ env.TAG }}" + + - name: Create GitHub Release + if: ${{ inputs.dry_run != true }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.TAG }} + name: ${{ env.TAG }} + body: ${{ steps.changelog.outputs.body }} + files: aryo-activity-log.${{ env.VERSION }}.zip + + - name: Deploy to WordPress.org + if: ${{ inputs.dry_run != true }} + uses: 10up/action-wordpress-plugin-deploy@stable + env: + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + SLUG: aryo-activity-log + VERSION: ${{ env.VERSION }} + BUILD_DIR: aryo-activity-log + + - name: Deploy to WordPress.org (dry run) + if: ${{ inputs.dry_run == true }} + uses: 10up/action-wordpress-plugin-deploy@stable + with: + dry-run: true + env: + SLUG: aryo-activity-log + VERSION: ${{ env.VERSION }} + BUILD_DIR: aryo-activity-log + + - name: Summary + run: | + if ${{ inputs.dry_run }}; then + echo "::notice::Dry run complete for v${{ env.VERSION }}. No commits, tags, or WP.org deploys were made." + else + echo "::notice::Released v${{ env.VERSION }} to GitHub and WordPress.org." + fi diff --git a/package.json b/package.json index 54373c2..44aab4e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "scripts": { "clean": "rimraf $npm_package_name $npm_package_name.*.zip", "package": "npm run clean && rsync -av --exclude-from=.build-rsync-exclude . $npm_package_name", - "package:zip": "npm run package && zip -r $npm_package_name.$npm_package_version.zip ./$npm_package_name/*" + "package:zip": "npm run package && zip -r $npm_package_name.$npm_package_version.zip ./$npm_package_name/*", + "version:next": "node scripts/next-version.js", + "version:sync": "node scripts/sync-version.js", + "version:validate-changelog": "node scripts/validate-readme-changelog.js" }, "devDependencies": { "rimraf": "^6.0.0" diff --git a/scripts/next-version.js b/scripts/next-version.js new file mode 100644 index 0000000..2f064ef --- /dev/null +++ b/scripts/next-version.js @@ -0,0 +1,30 @@ +'use strict'; + +const path = require( 'path' ); + +const bump = process.argv[ 2 ]; + +if ( ! [ 'patch', 'minor', 'major' ].includes( bump ) ) { + console.error( 'Usage: node next-version.js ' ); + process.exit( 1 ); +} + +const { version } = require( path.resolve( __dirname, '..', 'package.json' ) ); +const parts = version.split( '.' ).map( Number ); + +switch ( bump ) { + case 'major': + parts[ 0 ]++; + parts[ 1 ] = 0; + parts[ 2 ] = 0; + break; + case 'minor': + parts[ 1 ]++; + parts[ 2 ] = 0; + break; + case 'patch': + parts[ 2 ]++; + break; +} + +console.log( parts.join( '.' ) ); diff --git a/scripts/sync-version.js b/scripts/sync-version.js new file mode 100644 index 0000000..1fa9d02 --- /dev/null +++ b/scripts/sync-version.js @@ -0,0 +1,27 @@ +'use strict'; + +const fs = require( 'fs' ); +const path = require( 'path' ); + +const root = path.resolve( __dirname, '..' ); +const { version } = require( path.join( root, 'package.json' ) ); + +const pluginFile = path.join( root, 'aryo-activity-log.php' ); +let plugin = fs.readFileSync( pluginFile, 'utf8' ); + +plugin = plugin.replace( + /^(Version:\s*)\d+\.\d+\.\d+/m, + `$1${ version }` +); +fs.writeFileSync( pluginFile, plugin ); + +const readmeFile = path.join( root, 'readme.txt' ); +let readme = fs.readFileSync( readmeFile, 'utf8' ); + +readme = readme.replace( + /^Stable tag: \d+\.\d+\.\d+/m, + `Stable tag: ${ version }` +); +fs.writeFileSync( readmeFile, readme ); + +console.log( `Synced version ${ version } to aryo-activity-log.php and readme.txt` ); diff --git a/scripts/validate-build-files.sh b/scripts/validate-build-files.sh new file mode 100755 index 0000000..3aa873c --- /dev/null +++ b/scripts/validate-build-files.sh @@ -0,0 +1,73 @@ +#!/bin/bash +set -eo pipefail + +if [[ -z "$PLUGIN_SLUG" ]]; then + echo "Set the PLUGIN_SLUG env var" + exit 1 +fi + +if [[ -z "$PLUGIN_VERSION" ]]; then + echo "Set the PLUGIN_VERSION env var" + exit 1 +fi + +PLUGIN_PATH="${PLUGIN_SLUG}" + +if [[ -n "$GITHUB_WORKSPACE" ]]; then + PLUGIN_PATH="${GITHUB_WORKSPACE}/${PLUGIN_SLUG}" +fi + +if [[ ! -d "$PLUGIN_PATH" ]]; then + echo "BUILD_DIR '$PLUGIN_PATH' does not exist" + exit 1 +fi + +cd "$PLUGIN_PATH" + +PLUGIN_MAIN_FILE="${PLUGIN_SLUG}.php" + +if [[ ! -f "$PLUGIN_MAIN_FILE" ]]; then + echo "${PLUGIN_MAIN_FILE} does not exist in build dir" + exit 1 +fi + +if [[ ! -f "readme.txt" ]]; then + echo "readme.txt does not exist in build dir" + exit 1 +fi + +if [[ $(grep -c "Version: $PLUGIN_VERSION" "$PLUGIN_MAIN_FILE") -eq 0 ]]; then + echo "${PLUGIN_MAIN_FILE} does not contain Version: $PLUGIN_VERSION" + EXISTING_VERSION=$(sed -n 's/.*Version: \(.*\)/\1/p' "$PLUGIN_MAIN_FILE") + echo "Found: $EXISTING_VERSION" + exit 1 +fi + +if [[ $(grep -c "Stable tag: $PLUGIN_VERSION" "readme.txt") -eq 0 ]]; then + echo "readme.txt does not contain Stable tag: $PLUGIN_VERSION" + EXISTING_VERSION=$(sed -n 's/.*Stable tag: \(.*\)/\1/p' "readme.txt") + echo "Found: $EXISTING_VERSION" + exit 1 +fi + +DENY_LIST="tests vendor bin .github .git node_modules" + +for item in $DENY_LIST; do + if [[ -e "$item" ]]; then + echo "ERROR: '$item' must not be in the build directory" + exit 1 + fi +done + +DENY_FILES="phpunit.xml composer.json composer.lock package.json package-lock.json" + +for item in $DENY_FILES; do + if [[ -f "$item" ]]; then + echo "ERROR: '$item' must not be in the build directory" + exit 1 + fi +done + +echo "Build validation passed for ${PLUGIN_SLUG} v${PLUGIN_VERSION}" +echo "Contents:" +ls -la diff --git a/scripts/validate-readme-changelog.js b/scripts/validate-readme-changelog.js new file mode 100644 index 0000000..5ba908a --- /dev/null +++ b/scripts/validate-readme-changelog.js @@ -0,0 +1,53 @@ +'use strict'; + +const fs = require( 'fs' ); +const path = require( 'path' ); + +const root = path.resolve( __dirname, '..' ); + +const version = process.argv[ 2 ] || require( path.join( root, 'package.json' ) ).version; +const readme = fs.readFileSync( path.join( root, 'readme.txt' ), 'utf8' ); + +const changelogMarker = '== Changelog =='; +const changelogIndex = readme.indexOf( changelogMarker ); + +if ( changelogIndex === -1 ) { + console.error( 'readme.txt is missing "== Changelog ==" section.' ); + process.exit( 1 ); +} + +const changelogBody = readme.slice( changelogIndex + changelogMarker.length ); + +// Match "= X.Y.Z =" or "= X.Y.Z - YYYY-MM-DD =" (date suffix is optional) +const sectionRegex = new RegExp( + `= ${ version.replace( /\./g, '\\.' ) }(\\s+-\\s+\\d{4}-\\d{2}-\\d{2})?\\s*=` +); + +const headerMatch = changelogBody.match( sectionRegex ); + +if ( ! headerMatch ) { + console.error( + `readme.txt is missing a changelog section for version ${ version }.\n` + + `Add "= ${ version } =" (optionally with " - YYYY-MM-DD") with release notes under "== Changelog ==" before dispatching the release.` + ); + process.exit( 1 ); +} + +const headerIndex = changelogBody.indexOf( headerMatch[ 0 ] ); +const afterHeader = changelogBody.slice( headerIndex + headerMatch[ 0 ].length ); +const nextSectionMatch = afterHeader.match( /\n= \d+\.\d+\.\d+/ ); +const sectionContent = nextSectionMatch + ? afterHeader.slice( 0, nextSectionMatch.index ) + : afterHeader; + +const hasBullet = /^\* /m.test( sectionContent ); + +if ( ! hasBullet ) { + console.error( + `readme.txt changelog section for version ${ version } has no release notes.\n` + + `Add at least one "* ..." line under the section header before dispatching the release.` + ); + process.exit( 1 ); +} + +console.log( `Changelog section for version ${ version } is valid.` ); From 4174518d271816e2deca484768ad10bef326cef2 Mon Sep 17 00:00:00 2001 From: Yakir Sitbon Date: Sun, 23 Aug 2026 17:21:14 +0300 Subject: [PATCH 2/5] ai review --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cbe3f5b..62edcf7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,7 +48,7 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 with: php-version: '7.4' tools: composer:v2 @@ -162,7 +162,7 @@ jobs: - name: Create GitHub Release if: ${{ inputs.dry_run != true }} - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 with: tag_name: ${{ env.TAG }} name: ${{ env.TAG }} @@ -171,7 +171,7 @@ jobs: - name: Deploy to WordPress.org if: ${{ inputs.dry_run != true }} - uses: 10up/action-wordpress-plugin-deploy@stable + uses: 10up/action-wordpress-plugin-deploy@54bd289b8525fd23a5c365ec369185f2966529c2 # stable env: SVN_USERNAME: ${{ secrets.SVN_USERNAME }} SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} @@ -181,7 +181,7 @@ jobs: - name: Deploy to WordPress.org (dry run) if: ${{ inputs.dry_run == true }} - uses: 10up/action-wordpress-plugin-deploy@stable + uses: 10up/action-wordpress-plugin-deploy@54bd289b8525fd23a5c365ec369185f2966529c2 # stable with: dry-run: true env: From c5d93b9de1569e57bde271f06d6f9dcd3fd48df8 Mon Sep 17 00:00:00 2001 From: Yakir Sitbon Date: Sun, 23 Aug 2026 17:28:59 +0300 Subject: [PATCH 3/5] use exclude list --- scripts/validate-build-files.sh | 47 +++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/scripts/validate-build-files.sh b/scripts/validate-build-files.sh index 3aa873c..33b570f 100755 --- a/scripts/validate-build-files.sh +++ b/scripts/validate-build-files.sh @@ -11,11 +11,9 @@ if [[ -z "$PLUGIN_VERSION" ]]; then exit 1 fi -PLUGIN_PATH="${PLUGIN_SLUG}" - -if [[ -n "$GITHUB_WORKSPACE" ]]; then - PLUGIN_PATH="${GITHUB_WORKSPACE}/${PLUGIN_SLUG}" -fi +REPO_ROOT="${GITHUB_WORKSPACE:-.}" +PLUGIN_PATH="${REPO_ROOT}/${PLUGIN_SLUG}" +EXCLUDE_FILE="${REPO_ROOT}/.build-rsync-exclude" if [[ ! -d "$PLUGIN_PATH" ]]; then echo "BUILD_DIR '$PLUGIN_PATH' does not exist" @@ -50,23 +48,32 @@ if [[ $(grep -c "Stable tag: $PLUGIN_VERSION" "readme.txt") -eq 0 ]]; then exit 1 fi -DENY_LIST="tests vendor bin .github .git node_modules" - -for item in $DENY_LIST; do - if [[ -e "$item" ]]; then - echo "ERROR: '$item' must not be in the build directory" - exit 1 - fi -done - -DENY_FILES="phpunit.xml composer.json composer.lock package.json package-lock.json" - -for item in $DENY_FILES; do - if [[ -f "$item" ]]; then - echo "ERROR: '$item' must not be in the build directory" +if [[ ! -f "$EXCLUDE_FILE" ]]; then + echo "WARNING: $EXCLUDE_FILE not found, skipping exclude-list check" +else + ERRORS=0 + while IFS= read -r entry; do + # Skip empty lines, comments, and glob-only patterns (e.g. *.zip) + [[ -z "$entry" ]] && continue + [[ "$entry" == \#* ]] && continue + + # Strip trailing slash for directory entries + clean="${entry%/}" + + # Skip self-references and glob patterns + [[ "$clean" == "$PLUGIN_SLUG" ]] && continue + [[ "$clean" == *"*"* ]] && continue + + if [[ -e "$clean" ]]; then + echo "ERROR: '$clean' from .build-rsync-exclude found in build directory" + ERRORS=1 + fi + done < "$EXCLUDE_FILE" + + if [[ $ERRORS -ne 0 ]]; then exit 1 fi -done +fi echo "Build validation passed for ${PLUGIN_SLUG} v${PLUGIN_VERSION}" echo "Contents:" From 9833bddf439bdf7abba6f56ef19c65265fa73b68 Mon Sep 17 00:00:00 2001 From: Yakir Sitbon Date: Sun, 23 Aug 2026 17:31:08 +0300 Subject: [PATCH 4/5] wip --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 62edcf7..9baebbf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -124,7 +124,7 @@ jobs: run: | TAG="${{ env.TAG }}" - if ${{ inputs.dry_run }}; then + if [[ "${{ inputs.dry_run }}" == "true" ]]; then BODY="Dry-run release for version ${{ env.VERSION }}." else PREV=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "") @@ -147,7 +147,7 @@ jobs: echo "## What's Changed" echo "" echo "$BODY" - if [ -n "$PREV" ] && ! ${{ inputs.dry_run }}; then + if [ -n "$PREV" ] && [[ "${{ inputs.dry_run }}" != "true" ]]; then echo "" echo "**Full Changelog**: [\`${PREV}...${TAG}\`](${REPO_URL}/compare/${PREV}...${TAG})" fi @@ -191,7 +191,7 @@ jobs: - name: Summary run: | - if ${{ inputs.dry_run }}; then + if [[ "${{ inputs.dry_run }}" == "true" ]]; then echo "::notice::Dry run complete for v${{ env.VERSION }}. No commits, tags, or WP.org deploys were made." else echo "::notice::Released v${{ env.VERSION }} to GitHub and WordPress.org." From d00b1eef8f2bc3d90d1ced728fca1079f57e224a Mon Sep 17 00:00:00 2001 From: Yakir Sitbon Date: Mon, 24 Aug 2026 15:33:01 +0300 Subject: [PATCH 5/5] Apply changelog in the git release --- .github/workflows/release.yml | 33 ++++----------------- scripts/extract-changelog-section.js | 44 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 scripts/extract-changelog-section.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9baebbf..7ed76f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,40 +117,17 @@ jobs: if: ${{ inputs.dry_run != true }} run: git tag "${{ env.TAG }}" - - name: Generate changelog + - name: Extract changelog from readme id: changelog - env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} run: | - TAG="${{ env.TAG }}" - - if [[ "${{ inputs.dry_run }}" == "true" ]]; then - BODY="Dry-run release for version ${{ env.VERSION }}." - else - PREV=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "") - if [ -z "$PREV" ]; then - LOG=$(git log --no-merges --format="%H %s" "$TAG") - else - LOG=$(git log --no-merges --format="%H %s" "${PREV}..${TAG}") - fi - BODY=$(echo "$LOG" \ - | grep -v -E ' (bump: v|CI: |Internal: |Revert: )' \ - | while read -r hash msg; do - short=${hash:0:7} - msg=$(echo "$msg" | sed "s|(#\([0-9]*\))|[#\1](${REPO_URL}/pull/\1)|g") - echo "- [\`${short}\`](${REPO_URL}/commit/${hash}) ${msg}" - done) - fi + node scripts/extract-changelog-section.js "${{ env.VERSION }}" > /tmp/changelog_section.txt { echo "body<> "$GITHUB_OUTPUT" diff --git a/scripts/extract-changelog-section.js b/scripts/extract-changelog-section.js new file mode 100644 index 0000000..37d4b40 --- /dev/null +++ b/scripts/extract-changelog-section.js @@ -0,0 +1,44 @@ +'use strict'; + +const fs = require( 'fs' ); +const path = require( 'path' ); + +const root = path.resolve( __dirname, '..' ); +const version = process.argv[ 2 ] || require( path.join( root, 'package.json' ) ).version; +const readme = fs.readFileSync( path.join( root, 'readme.txt' ), 'utf8' ); + +const changelogMarker = '== Changelog =='; +const changelogIndex = readme.indexOf( changelogMarker ); + +if ( changelogIndex === -1 ) { + console.error( 'readme.txt is missing "== Changelog ==" section.' ); + process.exit( 1 ); +} + +const changelogBody = readme.slice( changelogIndex + changelogMarker.length ); + +const sectionRegex = new RegExp( + `= ${ version.replace( /\./g, '\\.' ) }(\\s+-\\s+\\d{4}-\\d{2}-\\d{2})?\\s*=` +); + +const headerMatch = changelogBody.match( sectionRegex ); + +if ( ! headerMatch ) { + console.error( `Changelog section for version ${ version } not found.` ); + process.exit( 1 ); +} + +const headerIndex = changelogBody.indexOf( headerMatch[ 0 ] ); +const afterHeader = changelogBody.slice( headerIndex + headerMatch[ 0 ].length ); +const nextSectionMatch = afterHeader.match( /\n= \d+\.\d+\.\d+/ ); +const sectionContent = nextSectionMatch + ? afterHeader.slice( 0, nextSectionMatch.index ) + : afterHeader; + +const lines = sectionContent + .trim() + .split( '\n' ) + .map( ( l ) => l.trim() ) + .filter( Boolean ); + +console.log( lines.join( '\n' ) );