Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions .github/workflows/generate_monthly_changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#/
# @license Apache-2.0
#
# Copyright (c) 2025 The Stdlib Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#/

# Workflow name:
name: generate_monthly_changelog

# Workflow triggers:
on:
# Run the workflow at midnight UTC on the first day of each month:
schedule:
- cron: '0 0 1 * *'

# Allow the workflow to be manually run:
workflow_dispatch:

# Global permissions:
permissions:
# Allow read-only access to the repository contents:
contents: read

# Workflow jobs:
jobs:
# Generate a monthly changelog:
generate-monthly-changelog:
# Define a display name:
name: 'Generate Monthly Changelog'

# Define the type of virtual host machine:
runs-on: ubuntu-latest

# Workflow steps:
steps:
- name: 'Checkout source repository'
# Pin action to full length commit SHA
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# Specify whether to remove untracked files before checking out the repository:
clean: false

# Limit clone depth to the most recent commit:
fetch-depth: 1

# Token for accessing the repository:
token: ${{ secrets.STDLIB_BOT_FGPAT_REPO_READ }}

# Avoid storing GitHub token in local Git configuration:
persist-credentials: false

# Install Node.js:
- name: 'Install Node.js'
# Pin action to full length commit SHA
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
with:
node-version: '20' # 'lts/*'
timeout-minutes: 5

# Install dependencies (accounting for possible network failures, etc, when installing node module dependencies):
- name: 'Install dependencies'
run: |
make install-node-modules || make install-node-modules || make install-node-modules
timeout-minutes: 15

- name: 'Checkout monthly changelog repository'
# Pin action to full length commit SHA
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
# Code coverage repository:
repository: 'stdlib-js/www-blog-monthly-changelog'

# File path to checkout to:
path: './www-blog-monthly-changelog'

# Specify whether to remove untracked files before checking out the repository:
clean: false

# Limit clone depth to the most recent commit:
fetch-depth: 1

# Token for accessing the repository:
token: ${{ secrets.STDLIB_BOT_FGPAT_REPO_READ }}

# Avoid storing GitHub token in local Git configuration:
persist-credentials: false

# Generate changelog for last month:
- name: 'Generate changelog for last month'
run: |
UNTIL=$(date +"%Y-%m-01")
node -e "
var generate = require( '@stdlib/_tools/changelog/generate' );
var changelog = generate( '@stdlib', {
'flags': {
'since': '$UNTIL - 1 week',
'until': '$UNTIL'
}
});
console.log( changelog.content );
" > ./www-blog-monthly-changelog/monthly_changelog_$UNTIL.md
# Import GPG key to sign commits:
- name: 'Import GPG key to sign commits'
# Pin action to full length commit SHA
uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 # v6.2.0
with:
gpg_private_key: ${{ secrets.STDLIB_BOT_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.STDLIB_BOT_GPG_PASSPHRASE }}
git_user_signingkey: true
git_commit_gpgsign: true

# Commit and push changes:
- name: 'Commit and push changes'
env:
REPO_GITHUB_TOKEN: ${{ secrets.STDLIB_BOT_PAT_REPO_WRITE }}
USER_NAME: stdlib-bot
run: |
cd ./www-blog-monthly-changelog
git config --local user.email "[email protected]"
git config --local user.name "stdlib-bot"
git add .
git commit -m "Add monthly changelog" || exit 0
git push "https://$USER_NAME:[email protected]/stdlib-js/www-blog-monthly-changelog.git" main
50 changes: 45 additions & 5 deletions lib/node_modules/@stdlib/_tools/changelog/generate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ limitations under the License.
var generate = require( '@stdlib/_tools/changelog/generate' );
```

#### generate( pkg\[, releaseType] )
#### generate( pkg\[, options] )

Generates a Markdown formatted changelog for a specified package.

Expand All @@ -44,13 +44,22 @@ The function returns an object with the following properties:
- **content**: Markdown formatted changelog.
- **releaseType**: release type (`null` if changelog is for a non-release).

To generate a changelog for an upcoming release, provide a valid release type as the second argument.
The function accepts the following `options`:

- **releaseType**: a release type for which to generate the changelog for.
- **flags**: `git log` options used to retrieve commits to generate the changelog for.

By default, the changelog is generated for a non-release. To generate a changelog for an upcoming release, provide a valid release type:

```javascript
var changelog = generate( '@stdlib/assert/contains', 'patch' );
var changelog = generate( '@stdlib/assert/contains', {
'releaseType': 'patch'
});
// returns {...}

changelog = generate( '@stdlib/assert/contains', 'minor' );
changelog = generate( '@stdlib/assert/contains', {
'releaseType': 'minor'
});
// returns {...}
```

Expand All @@ -66,12 +75,32 @@ The following release types are supported:
- `auto`: automatically determine the release type based on parsed commit messages.
- `none`: no release (equivalent to not specifying a release type).

Under the hood, the changelog leverages `git log` to retrieve the commits from which to assemble the changelog. The `flags` option allows passing any options to the `git log` command, e.g. to generate a changelog for a specified time interval or for an individual contributor.

```js
var changelog = generate( '@stdlib/ndarray', {
'flags': {
'since': 'last month'
}
});
// returns {...}

changelog = generate( '@stdlib/ndarray', {
'flags': {
'author': 'Athan Reines <[email protected]>'
}
});
// returns {...}
```

</section>

<!-- /.usage -->

<section class="notes">

- When `flags` are supplied, the generated changelog skips printing empty releases and uses a flat output format.

</section>

<!-- /.notes -->
Expand All @@ -92,7 +121,9 @@ var releaseType = changelog.releaseType;
// returns null

// Generate a changelog for a new release:
changelog = generate( '@stdlib/utils/curry', 'patch' );
changelog = generate( '@stdlib/utils/curry', {
'releaseType': 'patch'
});
content = changelog.content;
// returns '...'

Expand All @@ -103,6 +134,15 @@ releaseType = changelog.releaseType;
changelog = generate( '@stdlib/string/base' );
content = changelog.content;
// returns '...'

// Generate a changelog restricted to a single author:
changelog = generate( '@stdlib/string/base', {
'flags': {
'author': 'Athan Reines <[email protected]>'
}
});
content = changelog.content;
// returns '...'
```

</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ console.log( releaseType );
// => null

// Generate a changelog for a new release:
changelog = generate( '@stdlib/utils/curry', 'patch' );
changelog = generate( '@stdlib/utils/curry', {
'releaseType': 'patch'
});
content = changelog.content;
console.log( content );
// => '...'
Expand All @@ -45,3 +47,12 @@ changelog = generate( '@stdlib/string/base' );
content = changelog.content;
console.log( content );
// => '...'

// Generate a changelog restricted to a single author:
changelog = generate( '@stdlib/string/base', {
'flags': {
'author': 'Athan Reines <[email protected]>'
}
});
content = changelog.content;
console.log( content );
47 changes: 38 additions & 9 deletions lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
var npmReleases = require( './npm_releases.js' );
var sectionStart = require( './section_start.js' );
var sectionEnd = require( './section_end.js' );
var validate = require( './validate.js' );
var heading = require( './heading.js' );


Expand Down Expand Up @@ -208,7 +209,9 @@
* Generates a Markdown formatted changelog for a specified stdlib package.
*
* @param {string} pkg - package name
* @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, `premajor`, or `auto`)
* @param {Options} options - function options
* @param {string} [options.releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, `premajor`, or `auto`)
* @param {Options} [options.flags] - `git log` options used to retrieve commits
* @throws {TypeError} must provide a string
* @throws {Error} must provide a valid package name
* @throws {TypeError} must provide a recognized release type
Expand All @@ -227,18 +230,23 @@
* // returns {...}
*
* @example
* var changelog = generate( '@stdlib/utils/curry', 'patch' );
* var changelog = generate( '@stdlib/utils/curry', {
* 'releaseType': 'patch'
* });
* // returns {...}
*
* @example
* var changelog = generate( '@stdlib/utils/curry', 'major' );
* var changelog = generate( '@stdlib/utils/curry', {
* 'releaseType': 'major'
* });
* // returns {...}
*/
function generate( pkg, releaseType ) {
function generate( pkg, options ) {
var isNamespacePkg;
var releaseCommits;
var newestRelease;
var bySubpackage;
var releaseType;
var nextVersion;
var standalone;
var unreleased;
Expand All @@ -247,14 +255,24 @@
var commits;
var version;
var summary;
var opts;
var name;
var str;
var err;
var i;
var j;

if ( !isString( pkg ) ) {
throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) );
}
opts = {};
if ( arguments.length > 1 ) {
err = validate( opts, options );
if ( err ) {
throw err;
}
}

if ( pkg === '@stdlib' || pkg === '@stdlib/stdlib' ) {
// Case: root package
isNamespacePkg = true;
Expand All @@ -275,9 +293,16 @@
str = '# CHANGELOG\n\n';
str += '> Package changelog.\n\n';

commits = parseCommits({
'dir': join( STDLIB_LIB_DIR, pkg )
});
if ( opts.flags ) {
commits = parseCommits({
'dir': join( STDLIB_LIB_DIR, pkg ),
'flags': opts.flags
});
} else {
commits = parseCommits({
'dir': join( STDLIB_LIB_DIR, pkg )
});
}
if ( commits.length === 0 ) {
throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) );
}
Expand All @@ -291,6 +316,7 @@
commits.unreleased = [];
}

releaseType = opts.releaseType;
if ( releaseType === 'auto' ) {
releaseType = recommendVersionBump( commits.unreleased );

Expand All @@ -315,7 +341,7 @@
}
}

if ( isNamespacePkg ) {
if ( isNamespacePkg && !opts.flags ) {
str += releaseSectionStart( nextVersion );
str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n';
if ( commits.unreleased.length > 0 ) {
Expand Down Expand Up @@ -348,7 +374,7 @@
str += sectionEnd( 'release' );
}
}
if ( isNamespacePkg ) {
if ( isNamespacePkg && !opts.flags ) {
for ( i = releases.length-1; i >= 0; i-- ) {
version = releases[ i ][ 0 ];
str += releaseSectionStart( version );
Expand All @@ -361,9 +387,9 @@
str += heading( 'Packages', 3 );
for ( j = 0; j < pkgNames.length; j++ ) {
name = pkgNames[ j ];
summary = releaseSummary( bySubpackage[ name ], true, true );

Check warning on line 390 in lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

This line has a length of 81. Maximum allowed is 80
if ( summary ) {
str += packageSummaryWrapper( pkg, version, name, summary );

Check warning on line 392 in lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

This line has a length of 84. Maximum allowed is 80
}
}
str += sectionEnd( 'packages' );
Expand All @@ -381,6 +407,9 @@
version = releases[ i ][ 0 ];
summary = releaseSummary( commits[ version ] );
if ( !summary ) {
if ( opts.flags ) {
continue;
}
summary = PLACEHOLDER_SUMMARY;
}
str += releaseSectionStart( version );
Expand Down Expand Up @@ -413,7 +442,7 @@
releaseDate = new Date( releases[ i ][ 1 ] );

// Get the previous release date or set it to a very early date if none exists:
prevReleaseDate = ( i > 0 ) ? new Date( releases[ i-1 ][ 1 ] ) : new Date( 0 );

Check warning on line 445 in lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

This line has a length of 91. Maximum allowed is 80
if ( date <= releaseDate && date > prevReleaseDate ) {
return releases[ i ][ 0 ]; // version
}
Expand Down
Loading
Loading