Skip to content

feat(nodejs): ✨ handle engine versions and package installs (#617) #68

feat(nodejs): ✨ handle engine versions and package installs (#617)

feat(nodejs): ✨ handle engine versions and package installs (#617) #68

name: Auto-pull-request for yarn dependencies
on:
# Run on push to main
push:
branches:
- main
paths:
- .github/workflows/auto-pr-yarn-dependencies.yaml
- website/package.json
- website/yarn.lock
# Run weekly
schedule:
- cron: '0 0 * * Sun'
# Needed so we can run it manually
workflow_dispatch:
permissions:
contents: read
defaults:
run:
shell: bash
env:
# Which branch to use to make the pull request
PR_BRANCH: auto/yarn_update
# Whether or not to enable auto-merge
PR_AUTO_MERGE: true
jobs:
check-pull-request:
name: Check if the pull request exists
runs-on: ubuntu-latest
if: github.repository_owner == 'XaF'
permissions:
contents: read
pull-requests: read
outputs:
pull_request_state: ${{ env.PR_STATE }}
should_run: ${{ github.event_name != 'push' || env.PR_STATE == 'OPEN' }}
steps:
- name: Checkout commit
uses: actions/checkout@v4
- name: Check if pull request already exists
env:
GH_TOKEN: ${{ github.token }}
run: |
STATE=$(gh pr view "${{ env.PR_BRANCH }}" \
--repo "${{ github.repository }}" \
--json state --jq '.state' || \
echo "NOT FOUND")
echo "PR_STATE=${STATE}" | tee -a "$GITHUB_ENV"
run-yarn-update:
name: Update yarn dependencies
runs-on: ubuntu-latest
needs:
- check-pull-request
if: needs.check-pull-request.outputs.should_run == 'true'
outputs:
any_update: ${{ env.UPDATES }}
package_updates: ${{ env.PACKAGE_UPDATES }}
yarn_updates: ${{ env.YARN_UPDATES }}
steps:
- name: Checkout commit
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '>=16.14 <20.6.0 || >=20.6.1'
cache: yarn
cache-dependency-path: 'website/yarn.lock'
- name: npm check updates
working-directory: website/
run: |
npx npm-check-updates --upgrade | tee npm-check-updates.out
if grep --quiet 'yarn install' npm-check-updates.out; then
echo "NEEDS_YARN_INSTALL=true" | tee -a "$GITHUB_ENV"
fi
- name: yarn install
if: ${{ env.NEEDS_YARN_INSTALL == 'true' }}
working-directory: website/
run: |
yarn install
- name: yarn upgrade
working-directory: website/
run: |
yarn upgrade
npx yarn-deduplicate yarn.lock
- name: Check if there is any update
working-directory: website/
run : |
UPDATES=false
git diff --quiet package.json && \
PACKAGE_UPDATES=false || \
{ PACKAGE_UPDATES=true; UPDATES=true; }
git diff --quiet yarn.lock && \
YARN_UPDATES=false || \
{ YARN_UPDATES=true; UPDATES=true; }
echo "PACKAGE_UPDATES=${PACKAGE_UPDATES}" | tee -a "$GITHUB_ENV"
echo "YARN_UPDATES=${YARN_UPDATES}" | tee -a "$GITHUB_ENV"
echo "UPDATES=${UPDATES}" | tee -a "$GITHUB_ENV"
- name: upload package.json artifact for use in PR
if: ${{ env.PACKAGE_UPDATES == 'true' }}
uses: actions/upload-artifact@v4
with:
name: package-json
path: website/package.json
retention-days: 1
- name: upload yarn.lock artifact for use in PR
if: ${{ env.YARN_UPDATES == 'true' }}
uses: actions/upload-artifact@v4
with:
name: yarn-lock
path: website/yarn.lock
retention-days: 1
create-or-update-pr:
name: Create or update pull-request
runs-on: ubuntu-latest
needs:
- check-pull-request
- run-yarn-update
if: needs.check-pull-request.outputs.should_run == 'true' && needs.run-yarn-update.outputs.any_update == 'true'
env:
PR_TITLE: "chore(deps): 📦 yarn update"
steps:
- name: Create application token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.OMNICLI_APP_ID }}
private-key: ${{ secrets.OMNICLI_PRIVATE_KEY }}
- name: Checkout commit
uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
- name: Download package.json from update job
if: needs.run-yarn-update.outputs.package_updates == 'true'
uses: actions/download-artifact@v4
with:
name: package-json
path: website/
- name: Download yarn.lock from update job
if: needs.run-yarn-update.outputs.yarn_updates == 'true'
uses: actions/download-artifact@v4
with:
name: yarn-lock
path: website/
- name: Craft commit and pull request details
run: |
packages_diff=$(git diff website/package.json | \
gawk '
/^[-+]\s*"[^"]*":\s*"[^"]*"/ {
match($0, /([-+])\s*"([^"]*)":\s*"[^0-9]*([0-9][^"]*)"/, arr);
print "1," arr[1] "," arr[2] "," arr[3];
}')
yarn_lock_diff=$(git diff website/yarn.lock | \
gawk '
/^[-+ ]"?[^" ]+@[^" ]+"?:/ {
match($0, /^[-+ ]"?([^" ]+)@/, arr);
pkg=arr[1];
}
/^[-+] version "[^"]*"$/ {
match($0, /^([+-]) version "([^"]*)"$/, arr);
operation=arr[1];
version=arr[2];
print "0," operation "," pkg "," version;
}')
( echo "${packages_diff}"; echo "${yarn_lock_diff}" ) | \
sort -t, -k1,1rn -k3,3 -k2,2r | \
gawk -F, '
BEGIN {
pkg="";
prev_version="`null`";
new_version="`null`";
prev_version_raw="null";
new_version_raw="null";
cur="";
print "${{ env.PR_TITLE }}" > "commit_body.txt";
print "# Yarn updates" > "pull_request_body.md";
}
{
if (pkg != $3) {
if (pkg != "") {
print "- " pkg " (" prev_version_raw " -> " new_version_raw ")" >> "commit_body.txt";
print "- [`" pkg "`](" pkg_url ") (" prev_version " -> " new_version ")" >> "pull_request_body.md";
prev_version="`null`";
new_version="`null`";
prev_version_raw="null";
new_version_raw="null";
}
pkg=$3;
pkg_url="https://www.npmjs.com/package/" pkg;
}
if (cur != $1) {
print "\n" ($1 == 1 ? "Direct" : "Indirect") " dependencies:" >> "commit_body.txt";
print "\n## " ($1 == 1 ? "Direct" : "Indirect") " dependencies\n" >> "pull_request_body.md";
cur=$1;
}
if ($2 == "+") {
new_version="[`v" $4 "`](" pkg_url "/v/" $4 ")";
new_version_raw="v" $4;
} else if ($2 == "-") {
prev_version="[`v" $4 "`](" pkg_url "/v/" $4 ")";
prev_version_raw="v" $4;
}
}
END {
if (pkg != "") {
print "- " pkg " (" prev_version_raw " -> " new_version_raw ")" >> "commit_body.txt";
print "- [`" pkg "`](" pkg_url ") (" prev_version " -> " new_version ")" >> "pull_request_body.md";
}
}'
- name: Truncate pull request body if necessary
run: |
gawk -v limit=59990 '
{
print; count += length($0) + 1
}
count > limit {
print "\n`[snip]`";
exit
}' pull_request_body.md > pull_request_body_trunc.md
mv pull_request_body_trunc.md pull_request_body.md
- name: Print commit message
run: |
cat commit_body.txt
- name: Print pull request body
run: |
cat pull_request_body.md
- name: git commit
run: |
git config user.name github-actions
git config user.email [email protected]
git switch --force-create "${{ env.PR_BRANCH }}"
git add website/yarn.lock website/package.json
git commit --no-verify --file=commit_body.txt
- name: git push
run: |
git push --no-verify --force --set-upstream origin "${{ env.PR_BRANCH }}"
- name: Create or update pull-request
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
PR_STATE: ${{ needs.check-pull-request.outputs.pull_request_state }}
run: |
if [[ "$PR_STATE" != "OPEN" ]]; then
gh pr create \
--title "${{ env.PR_TITLE }}" \
--body-file pull_request_body.md \
--repo "${{ github.repository }}" \
--label dependencies \
--label javascript
else
gh pr edit "${{ env.PR_BRANCH }}" \
--title "${{ env.PR_TITLE }}" \
--body-file pull_request_body.md \
--repo "${{ github.repository }}" \
--add-label dependencies \
--add-label javascript
fi
- name: Enable auto-merge
if: ${{ env.PR_AUTO_MERGE == 'true' }}
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
NUM=$(gh pr view "${{ env.PR_BRANCH }}" \
--repo "${{ github.repository }}" \
--json number --jq '.number' || \
echo "")
if [[ -z "$NUM" ]]; then
echo "Failed to get PR number"
exit 1
fi
gh pr merge --squash --auto --body "" "$NUM"
close-pr:
name: Close pull-request
runs-on: ubuntu-latest
needs:
- check-pull-request
- run-yarn-update
if: needs.check-pull-request.outputs.should_run == 'true' && needs.run-yarn-update.outputs.any_update == 'false' && needs.check-pull-request.outputs.pull_request_state == 'OPEN'
env:
PR_CLOSING_COMMENT: "Superseded. No more dependencies to be updated for now."
steps:
- name: Create application token
uses: actions/create-github-app-token@v1
id: app-token
env:
APP_ID: ${{ secrets.OMNICLI_APP_ID }}
PRIVATE_KEY: ${{ secrets.OMNICLI_PRIVATE_KEY }}
with:
app-id: ${{ env.APP_ID }}
private-key: ${{ env.PRIVATE_KEY }}
- name: Close pull-request
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
gh pr close \
--title "${{ env.PR_TITLE }}" \
--comment "${{ env.PR_CLOSING_COMMENT }}" \
--delete-branch