Skip to content

Commit

Permalink
Merge branch 'stdlib-js:develop' into math-iter-unary
Browse files Browse the repository at this point in the history
  • Loading branch information
gunjjoshi authored Sep 26, 2024
2 parents b301b79 + efce610 commit e065b72
Show file tree
Hide file tree
Showing 1,174 changed files with 43,250 additions and 13,612 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/autoclose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,35 @@ jobs:
GH_TOKEN: ${{ secrets.STDLIB_BOT_GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.pull_request.number }}

# Define a job which closes a pull request if a pull request is considered stale:
stale:

# Define job name:
name: 'Check for stale label'

# Only run this job if the pull request has a specific label:
if: "${{ github.event.label.name == 'autoclose: Stale' }}"

# Define job permissions:
permissions:
contents: read
issues: write
pull-requests: write

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

# Define the sequence of job steps:
steps:
# Close the pull request:
- name: 'Close pull request'
run: gh pr close "$NUMBER" --comment "$BODY"
env:
GH_TOKEN: ${{ secrets.STDLIB_BOT_GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.pull_request.number }}
BODY: |
This pull request has been automatically closed because it has been inactive for an extended period after changes were requested. If you still wish to pursue this contribution, feel free to reopen the pull request or submit a new one.
We appreciate your interest in contributing to stdlib!
7 changes: 6 additions & 1 deletion .github/workflows/generate_pr_commit_message.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ name: generate_pr_commit_message

# Workflow triggers:
on:
pull_request:
pull_request_target:
types:
- labeled

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

# Allow write access to issues, assignees, labels, and milestones:
issues: write

# Allow write access to pull requests:
pull-requests: write

# Workflow jobs:
Expand Down
138 changes: 118 additions & 20 deletions .github/workflows/scripts/generate_pr_commit_message
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,24 @@ on_error() {
exit "$1"
}

# Function to resolve GitHub handle to name and email using .mailmap
# Resolves a name and email using .mailmap via git check-mailmap, falling back to the provided values if no match is found.
#
# $1 - name
# $2 - email
resolve_name_email() {
local name="$1"
local email="$2"
local resolved

resolved=$(git check-mailmap "$name <$email>" 2>/dev/null)
if [ -n "$resolved" ]; then
echo "$resolved"
else
echo "$name <$email>"
fi
}

# Resolves GitHub handle to name and email using .mailmap.
#
# $1 - GitHub handle
resolve_user() {
Expand All @@ -76,24 +93,47 @@ resolve_user() {
fi
}

# Function to make authenticated GitHub API requests
# Makes GitHub API requests.
#
# $1 - HTTP method (GET or POST)
# $2 - API endpoint
# $3 - Data for POST requests
# $3 - data for POST requests
github_api() {
local method="$1"
local endpoint="$2"
local data="$3"

if [ "$method" == "GET" ]; then
curl -s -H "Authorization: token $GITHUB_TOKEN" "$GITHUB_API_URL$endpoint"
elif [ "$method" == "POST" ]; then
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/json" -d "$data" "$GITHUB_API_URL$endpoint"
else
echo "Invalid HTTP method: $method"
on_error 1
# Initialize an array to hold curl headers:
local headers=()

# If GITHUB_TOKEN is set, add the Authorization header:
if [ -n "$GITHUB_TOKEN" ]; then
headers+=("-H" "Authorization: token $GITHUB_TOKEN")
fi

# Determine the HTTP method and construct the curl command accordingly...
case "$method" in
GET)
curl -s "${headers[@]}" "$GITHUB_API_URL$endpoint"
;;
POST)
# For POST requests, always set the Content-Type header>
headers+=("-H" "Content-Type: application/json")

# If data is provided, include it in the request:
if [ -n "$data" ]; then
curl -s -X POST "${headers[@]}" -d "$data" "$GITHUB_API_URL$endpoint"
else
# Handle cases where POST data is required but not provided:
echo "POST request requires data."
on_error 1
fi
;;
*)
echo "Invalid HTTP method: $method"
on_error 1
;;
esac
}

# Main execution sequence.
Expand All @@ -103,6 +143,10 @@ main() {
pr_title=$(echo "$pr_details" | jq -r '.title')
pr_body=$(echo "$pr_details" | jq -r '.body // ""')
pr_url=$(echo "$pr_details" | jq -r '.html_url')
pr_author_login=$(echo "$pr_details" | jq -r '.user.login')

# Resolve the PR author's name and email using .mailmap:
pr_author_resolved=$(resolve_user "$pr_author_login")

# Extract reviewers:
pr_reviews=$(github_api "GET" "/repos/$REPO_OWNER/$REPO_NAME/pulls/$pr_number/reviews")
Expand All @@ -111,15 +155,68 @@ main() {
# Fetch commits in the PR:
pr_commits=$(github_api "GET" "/repos/$REPO_OWNER/$REPO_NAME/pulls/$pr_number/commits")

# Extract co-authors from commits:
co_authors=$(echo "$pr_commits" | jq -r '.[].commit.message' | grep -i "Co-authored-by:" | awk -F': ' '{print $2}' | sort | uniq | paste -sd '\n' -)
# Extract co-authors from commit messages:
processed_co_authors=""
while IFS= read -r co_author_line; do
name_email=$(echo "$co_author_line" | sed -E 's/Co-authored-by:[[:space:]]*(.*)/\1/')
name=$(echo "$name_email" | sed -E 's/^(.*)<.*>$/\1/' | xargs)
email=$(echo "$name_email" | sed -E 's/^.*<(.*)>$/\1/' | xargs)
resolved_author=$(resolve_name_email "$name" "$email")

processed_co_authors+="Co-authored-by: $resolved_author"$'\n'
done <<< "$co_authors"

# Extract commit authors:
authors_info=$(echo "$pr_commits" | jq -r '.[] | .commit.author | "\(.name) <\(.email)>"' | sort -u | sed '/^ *<.*>/d' | sed '/^$/d')

# Process commit authors:
commit_authors=""
while IFS= read -r author_line; do
# Skip empty lines:
if [ -z "$author_line" ]; then
continue
fi

# Extract name and email:
name=$(echo "$author_line" | sed -E 's/^(.*)<.*>$/\1/' | xargs)
email=$(echo "$author_line" | sed -E 's/^.*<(.*)>$/\1/' | xargs)

# Resolve name and email using .mailmap:
resolved_author=$(resolve_name_email "$name" "$email")

# Skip if the resolved author matches the resolved PR author:
if [ "$resolved_author" == "$pr_author_resolved" ]; then
continue
fi

commit_authors+="$resolved_author"$'\n'
done <<< "$authors_info"

# Remove any empty lines and duplicates:
commit_authors=$(echo "$commit_authors" | sort -u | sed '/^$/d')

# Extract linked issues from PR body (e.g., #123)
# Prefix with 'Co-authored-by: ':
commit_authors_formatted=$(echo "$commit_authors" | sed 's/^/Co-authored-by: /' | sort -u)

# Combine co-authors and commit authors:
all_co_authors=$(echo -e "$co_authors\n$commit_authors_formatted" | sort -u | sed '/^$/d')

# Extract 'Signed-off-by' lines from commits:
signed_off_bys=$(echo "$pr_commits" | jq -r '.[].commit.message' | grep -Eio 'Signed-off-by:.*' | sort -u)

# Extract linked issues from PR body (e.g., #123):
issue_numbers=$(echo "$pr_body" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | sort | uniq)
closes_issues=""
ref_issues=""

# GitHub-supported closing keywords:
closing_keywords=("close" "closes" "closed" "fix" "fixes" "fixed" "resolve" "resolves" "resolved")

# Create a regex pattern from the keywords:
keywords_pattern=$(IFS='|'; echo "${closing_keywords[*]}")

for issue in $issue_numbers; do
if echo "$pr_body" | grep -qi "closes.*#$issue"; then
if echo "$pr_body" | grep -Eiq "(${keywords_pattern})([[:space:]]+|:)[[:space:]]*#${issue}\b"; then
closes_issues+="Closes: https://github.com/$REPO_OWNER/$REPO_NAME/issues/$issue\n"
else
ref_issues+="Ref: https://github.com/$REPO_OWNER/$REPO_NAME/issues/$issue\n"
Expand All @@ -138,18 +235,19 @@ main() {
if [ -n "$ref_issues" ]; then
commit_body+="\n$ref_issues"
fi
if [ -n "$co_authors" ]; then
commit_body+="\n$co_authors"
commit_body+="\n"
if [ -n "$all_co_authors" ]; then
commit_body+="\n$all_co_authors"
fi
for reviewer in $reviewers; do
resolved_reviewer=$(resolve_user "$reviewer")
commit_body+="\nReviewed-by: $resolved_reviewer"
done

# Add Signed-off-by line:
pr_author=$(echo "$pr_details" | jq -r '.user.login')
signed_off_by=$(resolve_user "$pr_author")
commit_body+="\nSigned-off-by: $signed_off_by"
# Include Signed-off-by lines if present in the commits:
if [ -n "$signed_off_bys" ]; then
commit_body+="\n$signed_off_bys"
fi

# Combine subject and body:
commit_message="$commit_subject\n\n$commit_body"
Expand Down
3 changes: 3 additions & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Chinmay Joshi <[email protected]> Chinmay J

# D

Debashis Maharana <[email protected]> <[email protected]>
Debashis Maharana <[email protected]> DebashisMaharana

Dorrin Sotoudeh <[email protected]> <[email protected]>
Dorrin Sotoudeh <[email protected]> dorrin-sot

Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Christopher Dambamuromo <[email protected]>
Dan Rose <[email protected]>
Daniel Killenberger <[email protected]>
Daniel Yu <[email protected]>
Debashis Maharana <[email protected]>
Dominik Moritz <[email protected]>
Dorrin Sotoudeh <[email protected]>
EuniceSim142 <[email protected]>
Expand Down Expand Up @@ -99,6 +100,7 @@ Xiaochuan Ye <[email protected]>
Yernar Yergaziyev <[email protected]>
naveen <[email protected]>
nishant-s7 <[email protected]>
olenkabilonizhka <[email protected]>
orimiles5 <[email protected]>
rainn <[email protected]>
rei2hu <[email protected]>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

// MODULES //

var join = require( 'path' ).join;
var spawn = require( 'child_process' ).spawnSync; // eslint-disable-line node/no-sync
var logger = require( 'debug' );
var contains = require( '@stdlib/assert/contains' );
var replace = require( '@stdlib/string/replace' );
var map = require( '@stdlib/utils/map' );
Expand All @@ -31,11 +34,32 @@ var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' );

// VARIABLES //

var debug = logger( 'changelog:generate:format-contributors' );
var RE_CO_AUTHORED_BY = /co-authored-by/i;
var RESOLVE_NAME_EMAIL_CMD = join( __dirname, '..', 'scripts', 'resolve_name_email.sh' );


// FUNCTIONS //

/**
* Resolves a Git user name and email address according to the .mailmap file.
*
* @private
* @param {string} pair - name and email pair
* @returns {string} canonical name and email address if found, otherwise the original input
*/
function resolveNameEmailPair( pair ) {
try {
debug( 'Attempting to resolve name and email: %s.', pair );
return spawn( RESOLVE_NAME_EMAIL_CMD, [ pair ], {
'stdio': [ 'pipe', 'pipe', 'ignore' ] // stdin, stdout, stderr
}).stdout.toString();
} catch ( err ) {
debug( 'Encountered an error resolving name and email: %s.', err.message );
return pair;
}
}

/**
* Extracts a list of contributors from a list of commits.
*
Expand Down Expand Up @@ -66,7 +90,8 @@ function extractContributors( commits ) {
if (
RE_CO_AUTHORED_BY.test( mention.action )
) {
author = replace( mention.ref, /\s*<[^>]+>\s*/, '' );
author = resolveNameEmailPair( mention.ref );
author = replace( author, /\s*<[^>]+>\s*/, '' );
if (
!contains( out, author ) &&
!contains( EXCLUDED_CONTRIBUTORS, author )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
#
# @license Apache-2.0
#
# Copyright (c) 2024 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.

# Script for resolving a name and email using .mailmap via `git check-mailmap`.
#
# Usage: resolve-mailmap.sh "name <email>"
#
# Arguments:
#
# name_email The name and email pair in the format "name <email>"
#

if [ -z "$1" ]; then
echo "Error: must provide a name and email in the format \"name <email>\"." >&2
exit 1
fi

name_email="$1"

resolved=$(git check-mailmap "$name_email" 2>/dev/null)
if [ -n "$resolved" ]; then
echo "$resolved"
else
echo "$name_email"
fi
Loading

0 comments on commit e065b72

Please sign in to comment.