Skip to content

Commit

Permalink
Add a release creation Github action
Browse files Browse the repository at this point in the history
  • Loading branch information
matiasbenedetto committed Oct 10, 2024
1 parent 50d7990 commit 90208ec
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 25 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
/.gitattributes export-ignore
/composer.json export-ignore
/composer.lock export-ignore
/package.json export-ignore
/update-version-and-changelog.js export-ignore

#
# Auto detect text files and perform LF normalization
Expand Down
74 changes: 49 additions & 25 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
name: Deploy to WordPress.org

on:
release:
types: [published]
pull_request:
types: [closed]
branches:
- trunk

jobs:
tag:
name: New release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: WordPress Plugin Deploy
id: deploy
uses: 10up/action-wordpress-plugin-deploy@stable
with:
generate-zip: true
env:
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
- name: Upload release asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ${{github.workspace}}/${{ github.event.repository.name }}.zip
asset_name: ${{ github.event.repository.name }}.zip
asset_content_type: application/zip
deploy-to-wordpress:
if: >
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'release/') &&
( contains(github.event.pull_request.head.ref, '/major') || contains(github.event.pull_request.head.ref, '/minor') || contains(github.event.pull_request.head.ref, '/patch') ) &&
( github.event.pull_request.user.login == 'github-actions[bot]' )
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install node dependencies
run: npm install

- name: Get New Version
id: get-version
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Create Tag and Release on GitHub
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=v${{ steps.get-version.outputs.VERSION }}
git tag $VERSION
git push origin $VERSION
gh release create $VERSION --generate-notes
- name: Deploy Plugin to WordPress Plugin Directory
uses: 10up/action-wordpress-plugin-deploy@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
VERSION: ${{ steps.get-version.outputs.VERSION }}

- name: WordPress.org plugin asset/readme update
uses: 10up/action-wordpress-plugin-asset-update@stable
env:
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
71 changes: 71 additions & 0 deletions .github/workflows/release-new-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Create new release PR

on:
workflow_dispatch:
inputs:
release_type:
description: 'Release type'
required: true
type: choice
options:
- major
- minor
- patch

jobs:
prepare-release:
if: github.event_name == 'workflow_dispatch'
name: Prepare Release PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- uses: actions/setup-node@v3
with:
node-version: 20

- name: Install node dependencies
run: npm install

- name: Compile Javascript App
run: npm run build

- name: Create version update branch
id: create-branch
run: |
BRANCH_NAME="release/$(date +%Y-%m-%d)/${{ github.event.inputs.release_type }}-release"
git checkout -b $BRANCH_NAME
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Update version and changelog
id: update-version
run: |
npm run update-version
echo "NEW_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
env:
RELEASE_TYPE: ${{ github.event.inputs.release_type }}

- name: Commit changes
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
git add .
git commit -m "Version bump & changelog update" --no-verify
git push --set-upstream origin ${{ steps.create-branch.outputs.BRANCH_NAME }}
- name: Create Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create \
--title "[Automation] New ${{ github.event.inputs.release_type }} Release: ${{ steps.update-version.outputs.NEW_VERSION }}" \
--base trunk \
--head ${{ steps.create-branch.outputs.BRANCH_NAME }} \
--label "Release: ${{ github.event.inputs.release_type }}" \
--body "
### Release PR 🤖
This is a release PR for version **${{ steps.update-version.outputs.NEW_VERSION }}**, run by **@${{ github.actor }}**.
It updates the version of the Plugin and adds changes since the last tag to the Changelog file.
Merging this PR will trigger a new release and update the Plugin in the WordPress Plugin Directory."
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "theme-check",
"version": "20231220",
"description": "Create a block-based theme",
"author": "The theme check plugin is an easy way to test your theme and make sure it’s up to spec with the latest theme review standards.",
"license": "GPL-2.0-or-later",
"keywords": [
"WordPress",
"theme"
],
"homepage": "https://wordpress.org/plugins/theme-check/",
"repository": "git+https://github.com/WordPress/theme-check.git",
"bugs": {
"url": "https://wordpress.org/support/plugin/theme-check/"
},
"engines": {
"node": ">=20.10.0",
"npm": ">=10.2.3"
},
"scripts": {
"update-version": "node update-version-and-changelog.js"
},
"devDependencies": {
"simple-git": "^3.26.0"
}
}
159 changes: 159 additions & 0 deletions update-version-and-changelog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* eslint-disable no-console */

/**
* External dependencies
*/
const fs = require( 'fs' );
const core = require( '@actions/core' );
const simpleGit = require( 'simple-git' );
const { promisify } = require( 'util' );
const exec = promisify( require( 'child_process' ).exec );

const git = simpleGit.default();

const releaseType = process.env.RELEASE_TYPE;

// Constants
const VALID_RELEASE_TYPES = [ 'major', 'minor', 'patch' ];
const MAIN_PLUGIN_FILE = 'theme-check.php';

// To get the merges since the last (previous) tag
async function getChangesSinceLastTag() {
try {
// Fetch all tags, sorted by creation date
const tagsResult = await git.tags( {
'--sort': '-creatordate',
} );
const tags = tagsResult.all;
if ( tags.length === 0 ) {
console.error( '❌ Error: No previous tags found.' );
return null;
}
const previousTag = tags[ 0 ]; // The most recent tag

// Now get the changes since this tag
const changes = await git.log( [ `${ previousTag }..HEAD` ] );
return changes;
} catch ( error ) {
throw error;
}
}

// To know if there are changes since the last tag.
// we are not using getChangesSinceGitTag because it returns the just the merges and not the commits.
// So for example if a hotfix was committed directly to trunk this function will detect it but getChangesSinceGitTag will not.
async function getHasChangesSinceGitTag( tag ) {
const changes = await git.log( [ `HEAD...${ tag }` ] );
return changes?.all?.length > 0;
}

async function updateVersion() {
if ( ! VALID_RELEASE_TYPES.includes( releaseType ) ) {
console.error(
'❌ Error: Release type is not valid. Valid release types are: major, minor, patch.'
);
process.exit( 1 );
}

if (
! fs.existsSync( './package.json' ) ||
! fs.existsSync( './package-lock.json' )
) {
console.error( '❌ Error: package.json or lock file not found.' );
process.exit( 1 );
}

if ( ! fs.existsSync( './readme.txt' ) ) {
console.error( '❌ Error: readme.txt file not found.' );
process.exit( 1 );
}

if ( ! fs.existsSync( `./${ MAIN_PLUGIN_FILE }` ) ) {
console.error( `❌ Error: ${ MAIN_PLUGIN_FILE } file not found.` );
process.exit( 1 );
}

// get changes since last tag
let changes = [];
try {
changes = await getChangesSinceLastTag();
} catch ( error ) {
console.error(
`❌ Error: failed to get changes since last tag: ${ error }`
);
process.exit( 1 );
}

const packageJson = require( './package.json' );
const currentVersion = packageJson.version;

// version bump package.json and package-lock.json using npm
const { stdout, stderr } = await exec(
`npm version --commit-hooks false --git-tag-version false ${ releaseType }`
);
if ( stderr ) {
console.error( `❌ Error: failed to bump the version."` );
process.exit( 1 );
}

const currentTag = `v${ currentVersion }`;
const newTag = stdout.trim();
const newVersion = newTag.replace( 'v', '' );
const hasChangesSinceGitTag = await getHasChangesSinceGitTag( currentTag );

// check if there are any changes
if ( ! hasChangesSinceGitTag ) {
console.error(
`❌ No changes since last tag (${ currentTag }). There is nothing new to release.`
);
// revert version update
await exec(
`npm version --commit-hooks false --git-tag-version false ${ currentVersion }`
);
process.exit( 1 );
}

console.info( '✅ Package.json version updated', currentTag, '=>', newTag );

// update readme.txt version with the new changelog
const readme = fs.readFileSync( './readme.txt', 'utf8' );
const capitalizeFirstLetter = ( string ) =>
string.charAt( 0 ).toUpperCase() + string.slice( 1 );

const changelogChanges = changes.all
.map(
( change ) =>
`* ${ capitalizeFirstLetter( change.message || change.body ) }`
)
.join( '\n' );
const newChangelog = `== Changelog ==\n\n= ${ newVersion } =\n${ changelogChanges }`;
let newReadme = readme.replace( '== Changelog ==', newChangelog );
// update version in readme.txt
newReadme = newReadme.replace(
/Stable tag: (.*)/,
`Stable tag: ${ newVersion }`
);
fs.writeFileSync( './readme.txt', newReadme );
console.info( '✅ Readme version updated', currentTag, '=>', newTag );

// update theme-check.php version
const pluginPhpFile = fs.readFileSync( `./${ MAIN_PLUGIN_FILE }`, 'utf8' );
const newPluginPhpFile = pluginPhpFile.replace(
/Version: (.*)/,
`Version: ${ newVersion }`
);
fs.writeFileSync( `./${ MAIN_PLUGIN_FILE }`, newPluginPhpFile );
console.info(
`✅ ${ MAIN_PLUGIN_FILE } file version updated`,
currentTag,
'=>',
newTag
);

// output data to be used by the next steps of the github action
core.setOutput( 'NEW_VERSION', newVersion );
core.setOutput( 'NEW_TAG', newTag );
core.setOutput( 'CHANGELOG', changelogChanges );
}

updateVersion();

0 comments on commit 90208ec

Please sign in to comment.