Update code-freeze tool to work with accelerated
This commit is contained in:
parent
703936e307
commit
4d6d57e63c
|
@ -1,7 +1,7 @@
|
||||||
name: 'Release: Code freeze'
|
name: 'Release: Code freeze'
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 23 * * 1' # Run at 2300 UTC on Mondays.
|
- cron: '0 0 * * 4' # Run at start of day UTC on Thursdays.
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
timeOverride:
|
timeOverride:
|
||||||
|
@ -31,10 +31,15 @@ jobs:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
outputs:
|
outputs:
|
||||||
freeze: ${{ steps.check-freeze.outputs.freeze }}
|
isTodayAcceleratedFreeze: ${{ steps.get-versions.outputs.isTodayAcceleratedFreeze }}
|
||||||
nextReleaseBranch: ${{ steps.branch.outputs.nextReleaseBranch }}
|
isTodayMonthlyFreeze: ${{ steps.get-versions.outputs.isTodayMonthlyFreeze }}
|
||||||
nextReleaseVersion: ${{ steps.milestone.outputs.nextReleaseVersion }}
|
acceleratedVersion: ${{ steps.get-versions.outputs.acceleratedVersion }}
|
||||||
nextDevelopmentVersion: ${{ steps.milestone.outputs.nextDevelopmentVersion }}
|
monthlyVersion: ${{ steps.get-versions.outputs.monthlyVersion }}
|
||||||
|
monthlyVersionXY: ${{ steps.get-versions.outputs.monthlyVersionXY }}
|
||||||
|
releasesFrozenToday: ${{ steps.get-versions.outputs.releasesFrozenToday }}
|
||||||
|
acceleratedBranch: ${{ steps.get-versions.outputs.acceleratedBranch }}
|
||||||
|
monthlyBranch: ${{ steps.get-versions.outputs.monthlyBranch }}
|
||||||
|
monthlyMilestone: ${{ steps.get-versions.outputs.monthlyMilestone }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -60,43 +65,74 @@ jobs:
|
||||||
pnpm build
|
pnpm build
|
||||||
working-directory: tools/monorepo-utils
|
working-directory: tools/monorepo-utils
|
||||||
|
|
||||||
- name: 'Check whether today is the code freeze day'
|
- name: 'Get the versions for the accelerated and monthly releases'
|
||||||
id: check-freeze
|
id: get-versions
|
||||||
run: pnpm utils code-freeze verify-day -o $TIME_OVERRIDE
|
run: pnpm utils code-freeze get-version -o $TIME_OVERRIDE
|
||||||
|
|
||||||
- name: Create next milestone
|
- name: Create next monthly milestone
|
||||||
id: milestone
|
id: milestone
|
||||||
if: steps.check-freeze.outputs.freeze == 'true'
|
if: steps.get-versions.outputs.isTodayMonthlyFreeze == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: pnpm run utils code-freeze milestone -o ${{ github.repository_owner }}
|
run: pnpm run utils code-freeze milestone -o ${{ github.repository_owner }} -m ${{ steps.get-versions.outputs.monthlyMilestone }}
|
||||||
|
|
||||||
- name: Create next release branch
|
- name: Create next monthly release branch
|
||||||
id: branch
|
id: branch
|
||||||
if: steps.check-freeze.outputs.freeze == 'true'
|
if: steps.get-versions.outputs.isTodayMonthlyFreeze == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: pnpm run utils code-freeze branch -o ${{ github.repository_owner }}
|
run: pnpm run utils code-freeze branch -o ${{ github.repository_owner }} -b ${{ steps.get-versions.outputs.monthlyBranch }}
|
||||||
|
|
||||||
|
- name: Create next accelerated release branch
|
||||||
|
id: branch-accel
|
||||||
|
if: steps.get-versions.outputs.isTodayAcceleratedFreeze == 'yes'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: pnpm run utils code-freeze branch -o ${{ github.repository_owner }} -b ${{ steps.get-versions.outputs.acceleratedBranch }}
|
||||||
|
|
||||||
|
- name: Bump versions for Beta.1 monthly release
|
||||||
|
id: version-bump
|
||||||
|
if: steps.get-versions.outputs.isTodayMonthlyFreeze == 'yes'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: pnpm run utils code-freeze version-bump -o ${{ github.repository_owner }} -b ${{ steps.get-versions.outputs.monthlyBranch }} -c ${{ steps.get-versions.outputs.monthlyVersion }}-beta.1
|
||||||
|
|
||||||
|
- name: Bump versions for accelerated release
|
||||||
|
id: version-bump-accel
|
||||||
|
if: steps.get-versions.outputs.isTodayAcceleratedFreeze == 'yes'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: pnpm run utils code-freeze version-bump -o ${{ github.repository_owner }} -b ${{ steps.get-versions.outputs.acceleratedBranch }} -c ${{ steps.get-versions.outputs.acceleratedVersion }} -af
|
||||||
|
|
||||||
|
- name: Prep accelerated release
|
||||||
|
id: accel-release-prep
|
||||||
|
if: steps.get-versions.outputs.isTodayAcceleratedFreeze == 'yes'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: pnpm utils code-freeze accelerated-prep -o ${{ github.repository_owner }} -b ${{ steps.get-versions.outputs.acceleratedBranch }} -c ${{ steps.get-versions.outputs.acceleratedVersion }} ${{ steps.get-versions.outputs.acceleratedReleaseDate }}
|
||||||
|
|
||||||
- name: Prepare trunk for next development cycle
|
- name: Prepare trunk for next development cycle
|
||||||
id: prep-trunk
|
id: prep-trunk
|
||||||
if: steps.check-freeze.outputs.freeze == 'true'
|
if: steps.get-versions.outputs.isTodayMonthlyFreeze == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: pnpm run utils code-freeze version-bump ${{ steps.milestone.outputs.nextDevelopmentVersion }}.0-dev -o ${{ github.repository_owner }}
|
run: pnpm run utils code-freeze version-bump ${{ steps.get-versions.outputs.monthlyMilestone }}-dev -o ${{ github.repository_owner }}
|
||||||
|
|
||||||
- name: Generate changelog changes
|
- name: Generate changelog changes
|
||||||
id: changelog
|
id: changelog
|
||||||
if: steps.check-freeze.outputs.freeze == 'true'
|
if: steps.get-versions.outputs.isTodayMonthlyFreeze == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: pnpm run utils code-freeze changelog -o ${{ github.repository_owner }} -v ${{ steps.milestone.outputs.nextReleaseVersion }}
|
run: pnpm run utils code-freeze changelog -c -o ${{ github.repository_owner }} -v ${{ steps.get-versions.outputs.monthlyVersionXY }}
|
||||||
|
|
||||||
|
|
||||||
notify-slack:
|
notify-slack:
|
||||||
name: 'Sends code freeze notification to Slack'
|
name: 'Sends code freeze notification to Slack'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs: code-freeze-prep
|
needs: code-freeze-prep
|
||||||
if: ${{ needs.code-freeze-prep.outputs.freeze == 'true' && inputs.skipSlackPing != true }}
|
if: ${{ inputs.skipSlackPing != true && ( needs.code-freeze-prep.outputs.isTodayAcceleratedFreeze == 'yes' || needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' ) }}
|
||||||
|
outputs:
|
||||||
|
ts: ${{ steps.notify.outputs.ts }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -126,6 +162,210 @@ jobs:
|
||||||
id: notify
|
id: notify
|
||||||
run: |
|
run: |
|
||||||
pnpm utils slack "${{ secrets.CODE_FREEZE_BOT_TOKEN }}" "
|
pnpm utils slack "${{ secrets.CODE_FREEZE_BOT_TOKEN }}" "
|
||||||
:warning-8c: ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} Code Freeze :ice_cube:
|
:warning-8c: ${{ join( fromJSON( needs.code-freeze-prep.outputs.releasesFrozenToday ), ' and ' ) }} Code Freeze :ice_cube:
|
||||||
The automation to cut the release branch for ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} has run. Any PRs that were not already merged will be a part of ${{ needs.code-freeze-prep.outputs.nextDevelopmentVersion }} by default. If you have something that needs to make ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} that hasn't yet been merged, please see the <${{ secrets.FG_LINK }}/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>.
|
The freeze automation for ${{ join( fromJSON( needs.code-freeze-prep.outputs.releasesFrozenToday ), ' and ' ) }} has finished. ${{ ( needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' && 'If you need to request a code freeze exception, see the <' ) || '' }}${{ ( needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' && secrets.FG_LINK ) || '' }}${{ ( needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' && '/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>.' ) || '' }}
|
||||||
|
|
||||||
|
The build for ${{ join( fromJSON( needs.code-freeze-prep.outputs.releasesFrozenToday ), ' and ' ) }} will appear in this thread shortly... :thread:
|
||||||
" "${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}"
|
" "${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}"
|
||||||
|
|
||||||
|
build-monthly:
|
||||||
|
name: Build beta zip file
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: code-freeze-prep
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.code-freeze-prep.outputs.monthlyBranch }}
|
||||||
|
|
||||||
|
- name: Setup WooCommerce Monorepo
|
||||||
|
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||||
|
with:
|
||||||
|
build: false
|
||||||
|
|
||||||
|
- name: Build zip
|
||||||
|
working-directory: plugins/woocommerce
|
||||||
|
run: bash bin/build-zip.sh
|
||||||
|
|
||||||
|
- name: Upload the zip file as an artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1
|
||||||
|
path: plugins/woocommerce/woocommerce.zip
|
||||||
|
retention-days: 2
|
||||||
|
|
||||||
|
build-a:
|
||||||
|
name: Build accelerated zip file
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: code-freeze-prep
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayAcceleratedFreeze == 'yes' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.code-freeze-prep.outputs.acceleratedBranch }}
|
||||||
|
|
||||||
|
- name: Setup WooCommerce Monorepo
|
||||||
|
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||||
|
with:
|
||||||
|
build: false
|
||||||
|
|
||||||
|
- name: Build zip
|
||||||
|
working-directory: plugins/woocommerce
|
||||||
|
run: bash bin/build-zip.sh
|
||||||
|
|
||||||
|
- name: Upload the zip file as an artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.acceleratedVersion }}
|
||||||
|
path: plugins/woocommerce/woocommerce.zip
|
||||||
|
retention-days: 2
|
||||||
|
|
||||||
|
slack-upload-monthly:
|
||||||
|
name: Upload Beta to Slack
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [ code-freeze-prep, notify-slack, build-monthly ]
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' && inputs.skipSlackPing != true }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup PNPM
|
||||||
|
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
|
||||||
|
with:
|
||||||
|
version: '8.6.7'
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
cache: pnpm
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Install prerequisites
|
||||||
|
run: |
|
||||||
|
pnpm install --filter monorepo-utils --ignore-scripts
|
||||||
|
# ignore scripts speeds up setup signficantly, but we still need to build monorepo utils
|
||||||
|
pnpm build
|
||||||
|
working-directory: tools/monorepo-utils
|
||||||
|
|
||||||
|
- id: download
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1
|
||||||
|
path: download
|
||||||
|
|
||||||
|
- run: ls -lah ${{steps.download.outputs.download-path}}
|
||||||
|
|
||||||
|
- name: Send release zip to Slack
|
||||||
|
id: send-file-slack
|
||||||
|
run : |
|
||||||
|
pnpm utils slack file "${{ secrets.CODE_FREEZE_BOT_TOKEN }}" "Here's the generated release build for ${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1" "${{ steps.download.outputs.download-path }}/woocommerce.zip" "${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}" --reply-ts ${{ needs.notify-slack.outputs.ts }} --filename "woocommerce.${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1.zip"
|
||||||
|
|
||||||
|
slack-upload-accelerated:
|
||||||
|
name: Upload Accelerated to Slack
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [ code-freeze-prep, notify-slack, build-a ]
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayAcceleratedFreeze == 'yes' && inputs.skipSlackPing != true }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup PNPM
|
||||||
|
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
|
||||||
|
with:
|
||||||
|
version: '8.6.7'
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
cache: pnpm
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Install prerequisites
|
||||||
|
run: |
|
||||||
|
pnpm install --filter monorepo-utils --ignore-scripts
|
||||||
|
# ignore scripts speeds up setup signficantly, but we still need to build monorepo utils
|
||||||
|
pnpm build
|
||||||
|
working-directory: tools/monorepo-utils
|
||||||
|
|
||||||
|
- id: download
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.acceleratedVersion }}
|
||||||
|
path: download
|
||||||
|
|
||||||
|
- run: ls -lah ${{steps.download.outputs.download-path}}
|
||||||
|
|
||||||
|
- name: Send release zip to Slack
|
||||||
|
id: send-file-slack
|
||||||
|
run : |
|
||||||
|
pnpm utils slack file "${{ secrets.CODE_FREEZE_BOT_TOKEN }}" "Here's the generated release build for ${{ needs.code-freeze-prep.outputs.acceleratedVersion }}" "${{ steps.download.outputs.download-path }}/woocommerce.zip" "${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}" --reply-ts ${{ needs.notify-slack.outputs.ts }} --filename "woocommerce.${{ needs.code-freeze-prep.outputs.acceleratedVersion }}.zip"
|
||||||
|
|
||||||
|
github-upload-monthly:
|
||||||
|
name: Create single-zipped GitHub asset (Monthly)
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [ code-freeze-prep, build-monthly ]
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayMonthlyFreeze == 'yes' }}
|
||||||
|
steps:
|
||||||
|
- id: download
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1
|
||||||
|
path: download
|
||||||
|
|
||||||
|
- name: Unzip the file (prevents double zip problem)
|
||||||
|
run: unzip ${{ steps.download.outputs.download-path }}/woocommerce.zip -d zipfile
|
||||||
|
|
||||||
|
- name: Upload the zip file as an artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: woocommerce.${{ needs.code-freeze-prep.outputs.monthlyVersion }}-beta.1
|
||||||
|
path: zipfile
|
||||||
|
retention-days: 10
|
||||||
|
|
||||||
|
github-upload-accelerated:
|
||||||
|
name: Create single-zipped GitHub asset (Accelerated)
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [ code-freeze-prep, build-a ]
|
||||||
|
if: ${{ needs.code-freeze-prep.outputs.isTodayAcceleratedFreeze == 'yes' }}
|
||||||
|
steps:
|
||||||
|
- id: download
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: double-zipped-woocommerce.${{ needs.code-freeze-prep.outputs.acceleratedVersion }}
|
||||||
|
path: download
|
||||||
|
|
||||||
|
- name: Unzip the file (prevents double zip problem)
|
||||||
|
run: unzip ${{ steps.download.outputs.download-path }}/woocommerce.zip -d zipfile
|
||||||
|
|
||||||
|
- name: Upload the zip file as an artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: woocommerce.${{ needs.code-freeze-prep.outputs.acceleratedVersion }}
|
||||||
|
path: zipfile
|
||||||
|
retention-days: 10
|
1331
pnpm-lock.yaml
1331
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,7 @@
|
||||||
"@octokit/graphql": "4.8.0",
|
"@octokit/graphql": "4.8.0",
|
||||||
"@octokit/graphql-schema": "^14.1.0",
|
"@octokit/graphql-schema": "^14.1.0",
|
||||||
"@octokit/types": "^9.2.0",
|
"@octokit/types": "^9.2.0",
|
||||||
|
"@slack/web-api": "^6.9.0",
|
||||||
"@types/cli-table": "^0.3.1",
|
"@types/cli-table": "^0.3.1",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
|
@ -24,13 +25,13 @@
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"luxon": "^3.4.4",
|
||||||
"octokit": "^2.0.14",
|
"octokit": "^2.0.14",
|
||||||
"ora": "^5.4.1",
|
"ora": "^5.4.1",
|
||||||
"promptly": "^3.2.0",
|
"promptly": "^3.2.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"simple-git": "^3.10.0",
|
"simple-git": "^3.10.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0"
|
||||||
"@slack/web-api": "^6.9.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
import simpleGit from 'simple-git';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../core/logger';
|
||||||
|
import {
|
||||||
|
sparseCheckoutRepoShallow,
|
||||||
|
checkoutRemoteBranch,
|
||||||
|
} from '../../../core/git';
|
||||||
|
import { createPullRequest } from '../../../core/github/repo';
|
||||||
|
import { getEnvVar } from '../../../core/environment';
|
||||||
|
import { Options } from './types';
|
||||||
|
import { addHeader, createChangelog } from './lib/prep';
|
||||||
|
|
||||||
|
export const acceleratedPrepCommand = new Command( 'accelerated-prep' )
|
||||||
|
.description( 'Prep for an accelerated release' )
|
||||||
|
.argument( '<version>', 'Version to bump to use for changelog' )
|
||||||
|
.argument( '<date>', 'Release date to use in changelog' )
|
||||||
|
.option(
|
||||||
|
'-o --owner <owner>',
|
||||||
|
'Repository owner. Default: woocommerce',
|
||||||
|
'woocommerce'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-n --name <name>',
|
||||||
|
'Repository name. Default: woocommerce',
|
||||||
|
'woocommerce'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-b --base <base>',
|
||||||
|
'Base branch to create the PR against. Default: trunk',
|
||||||
|
'trunk'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-d --dry-run',
|
||||||
|
'Prepare the version bump and log a diff. Do not create a PR or push to branch',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-c --commit-direct-to-base',
|
||||||
|
'Commit directly to the base branch. Do not create a PR just push directly to base branch',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
.action( async ( version, date, options: Options ) => {
|
||||||
|
const { owner, name, base, dryRun, commitDirectToBase } = options;
|
||||||
|
|
||||||
|
Logger.startTask(
|
||||||
|
`Making a temporary clone of '${ owner }/${ name }'`
|
||||||
|
);
|
||||||
|
|
||||||
|
const source = `github.com/${ owner }/${ name }`;
|
||||||
|
const token = getEnvVar( 'GITHUB_TOKEN', true );
|
||||||
|
const remote = `https://${ owner }:${ token }@${ source }`;
|
||||||
|
const tmpRepoPath = await sparseCheckoutRepoShallow(
|
||||||
|
remote,
|
||||||
|
'woocommerce',
|
||||||
|
[
|
||||||
|
'plugins/woocommerce/includes/class-woocommerce.php',
|
||||||
|
// All that's needed is the line above, but including these here for completeness.
|
||||||
|
'plugins/woocommerce/composer.json',
|
||||||
|
'plugins/woocommerce/package.json',
|
||||||
|
'plugins/woocommerce/readme.txt',
|
||||||
|
'plugins/woocommerce/woocommerce.php',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
Logger.endTask();
|
||||||
|
|
||||||
|
Logger.notice(
|
||||||
|
`Temporary clone of '${ owner }/${ name }' created at ${ tmpRepoPath }`
|
||||||
|
);
|
||||||
|
|
||||||
|
const git = simpleGit( {
|
||||||
|
baseDir: tmpRepoPath,
|
||||||
|
config: [ 'core.hooksPath=/dev/null' ],
|
||||||
|
} );
|
||||||
|
|
||||||
|
const branch = `prep/${ base }-accelerated`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ( commitDirectToBase ) {
|
||||||
|
if ( base === 'trunk' ) {
|
||||||
|
Logger.error(
|
||||||
|
`The --commit-direct-to-base option cannot be used with the trunk branch as a base. A pull request must be created instead.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Logger.notice( `Checking out ${ base }` );
|
||||||
|
await checkoutRemoteBranch( tmpRepoPath, base );
|
||||||
|
} else {
|
||||||
|
const exists = await git.raw( 'ls-remote', 'origin', branch );
|
||||||
|
|
||||||
|
if ( ! dryRun && exists.trim().length > 0 ) {
|
||||||
|
Logger.error(
|
||||||
|
`Branch ${ branch } already exists. Run \`git push <remote> --delete ${ branch }\` and rerun this command.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( base !== 'trunk' ) {
|
||||||
|
// if the base is not trunk, we need to checkout the base branch first before creating a new branch.
|
||||||
|
Logger.notice( `Checking out ${ base }` );
|
||||||
|
await checkoutRemoteBranch( tmpRepoPath, base );
|
||||||
|
}
|
||||||
|
Logger.notice( `Creating new branch ${ branch }` );
|
||||||
|
await git.checkoutBranch( branch, base );
|
||||||
|
}
|
||||||
|
|
||||||
|
const workingBranch = commitDirectToBase ? base : branch;
|
||||||
|
|
||||||
|
Logger.notice(
|
||||||
|
`Adding Woo header to main plugin file and creating changelog.txt on ${ workingBranch } branch`
|
||||||
|
);
|
||||||
|
addHeader( tmpRepoPath );
|
||||||
|
createChangelog( tmpRepoPath, version, date );
|
||||||
|
|
||||||
|
if ( dryRun ) {
|
||||||
|
const diff = await git.diffSummary();
|
||||||
|
Logger.notice(
|
||||||
|
`The prep has been completed in the following files:`
|
||||||
|
);
|
||||||
|
Logger.warn( diff.files.map( ( f ) => f.file ).join( '\n' ) );
|
||||||
|
Logger.notice(
|
||||||
|
'Dry run complete. No pull was request created nor was a commit made.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.notice( 'Adding and committing changes' );
|
||||||
|
await git.add( '.' );
|
||||||
|
await git.commit(
|
||||||
|
`Add Woo header to main plugin file and create changelog in ${ base }`
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger.notice( `Pushing ${ workingBranch } branch to Github` );
|
||||||
|
await git.push( 'origin', workingBranch );
|
||||||
|
|
||||||
|
if ( ! commitDirectToBase ) {
|
||||||
|
Logger.startTask( 'Creating a pull request' );
|
||||||
|
|
||||||
|
const pullRequest = await createPullRequest( {
|
||||||
|
owner,
|
||||||
|
name,
|
||||||
|
title: `Add Woo header to main plugin file and create changelog in ${ base }`,
|
||||||
|
body: `This PR adds the Woo header to the main plugin file and creates a changelog.txt file in ${ base }.`,
|
||||||
|
head: branch,
|
||||||
|
base,
|
||||||
|
} );
|
||||||
|
Logger.notice(
|
||||||
|
`Pull request created: ${ pullRequest.html_url }`
|
||||||
|
);
|
||||||
|
Logger.endTask();
|
||||||
|
}
|
||||||
|
} catch ( error ) {
|
||||||
|
Logger.error( error );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { readFile, writeFile } from 'fs/promises';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../../core/logger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Woo header to main plugin file.
|
||||||
|
*
|
||||||
|
* @param tmpRepoPath cloned repo path
|
||||||
|
*/
|
||||||
|
export const addHeader = async ( tmpRepoPath: string ): Promise< void > => {
|
||||||
|
const filePath = join( tmpRepoPath, 'plugins/woocommerce/woocommerce.php' );
|
||||||
|
try {
|
||||||
|
const pluginFileContents = await readFile( filePath, 'utf8' );
|
||||||
|
|
||||||
|
const updatedPluginFileContents = pluginFileContents.replace(
|
||||||
|
' * @package WooCommerce\n */',
|
||||||
|
' *\n * Woo: 18734002369816:624a1b9ba2fe66bb06d84bcdd401c6a6\n *\n * @package WooCommerce\n */'
|
||||||
|
);
|
||||||
|
|
||||||
|
await writeFile( filePath, updatedPluginFileContents );
|
||||||
|
} catch ( e ) {
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create changelog file.
|
||||||
|
*
|
||||||
|
* @param tmpRepoPath cloned repo path
|
||||||
|
* @param version version for the changelog file
|
||||||
|
* @param date date of the release (Y-m-d)
|
||||||
|
*/
|
||||||
|
export const createChangelog = async (
|
||||||
|
tmpRepoPath: string,
|
||||||
|
version: string,
|
||||||
|
date: string
|
||||||
|
): Promise< void > => {
|
||||||
|
const filePath = join( tmpRepoPath, 'plugins/woocommerce/changelog.txt' );
|
||||||
|
try {
|
||||||
|
const changelogContents = `*** WooCommerce ***
|
||||||
|
|
||||||
|
${ date } - Version ${ version }
|
||||||
|
* Update - Deploy of WOoCommerce ${ version }
|
||||||
|
`;
|
||||||
|
|
||||||
|
await writeFile( filePath, changelogContents );
|
||||||
|
} catch ( e ) {
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
export type Options = {
|
||||||
|
owner?: string;
|
||||||
|
name?: string;
|
||||||
|
base?: string;
|
||||||
|
dryRun?: boolean;
|
||||||
|
commitDirectToBase?: boolean;
|
||||||
|
};
|
|
@ -28,6 +28,11 @@ export const changelogCommand = new Command( 'changelog' )
|
||||||
'-d --dev-repo-path <devRepoPath>',
|
'-d --dev-repo-path <devRepoPath>',
|
||||||
'Path to existing repo. Use this option to avoid cloning a fresh repo for development purposes. Note that using this option assumes dependencies are already installed.'
|
'Path to existing repo. Use this option to avoid cloning a fresh repo for development purposes. Note that using this option assumes dependencies are already installed.'
|
||||||
)
|
)
|
||||||
|
.option(
|
||||||
|
'-c --commit-direct-to-base',
|
||||||
|
'Commit directly to the base branch. Do not create a PR just push directly to base branch',
|
||||||
|
false
|
||||||
|
)
|
||||||
.option(
|
.option(
|
||||||
'-o, --override <override>',
|
'-o, --override <override>',
|
||||||
"Time Override: The time to use in checking whether the action should run (default: 'now').",
|
"Time Override: The time to use in checking whether the action should run (default: 'now').",
|
||||||
|
@ -39,10 +44,15 @@ export const changelogCommand = new Command( 'changelog' )
|
||||||
Logger.startTask(
|
Logger.startTask(
|
||||||
`Making a temporary clone of '${ owner }/${ name }'`
|
`Making a temporary clone of '${ owner }/${ name }'`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const cloneOptions = {
|
||||||
|
owner: owner ? owner : 'woocommerce',
|
||||||
|
name: name ? name : 'woocommerce',
|
||||||
|
};
|
||||||
// Use a supplied path, otherwise do a full clone of the repo, including history so that changelogs can be created with links to PRs.
|
// Use a supplied path, otherwise do a full clone of the repo, including history so that changelogs can be created with links to PRs.
|
||||||
const tmpRepoPath = devRepoPath
|
const tmpRepoPath = devRepoPath
|
||||||
? devRepoPath
|
? devRepoPath
|
||||||
: await cloneAuthenticatedRepo( options, false );
|
: await cloneAuthenticatedRepo( cloneOptions, false );
|
||||||
|
|
||||||
Logger.endTask();
|
Logger.endTask();
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,10 @@ import { Logger } from '../../../../core/logger';
|
||||||
import { checkoutRemoteBranch } from '../../../../core/git';
|
import { checkoutRemoteBranch } from '../../../../core/git';
|
||||||
import { createPullRequest } from '../../../../core/github/repo';
|
import { createPullRequest } from '../../../../core/github/repo';
|
||||||
import { Options } from '../types';
|
import { Options } from '../types';
|
||||||
import { getToday } from '../../verify-day/utils';
|
import {
|
||||||
|
getToday,
|
||||||
|
DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE,
|
||||||
|
} from '../../get-version/lib';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform changelog adjustments after Jetpack Changelogger has run.
|
* Perform changelog adjustments after Jetpack Changelogger has run.
|
||||||
|
@ -27,9 +30,10 @@ const updateReleaseChangelogs = async (
|
||||||
) => {
|
) => {
|
||||||
const today = getToday( override );
|
const today = getToday( override );
|
||||||
|
|
||||||
// The release date is 22 days after the code freeze.
|
const releaseTime = today.plus( {
|
||||||
const releaseTime = new Date( today.getTime() + 22 * 24 * 60 * 60 * 1000 );
|
days: DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE,
|
||||||
const releaseDate = releaseTime.toISOString().split( 'T' )[ 0 ];
|
} );
|
||||||
|
const releaseDate = releaseTime.toISODate();
|
||||||
|
|
||||||
const readmeFile = path.join(
|
const readmeFile = path.join(
|
||||||
tmpRepoPath,
|
tmpRepoPath,
|
||||||
|
@ -79,7 +83,7 @@ export const updateReleaseBranchChangelogs = async (
|
||||||
tmpRepoPath: string,
|
tmpRepoPath: string,
|
||||||
releaseBranch: string
|
releaseBranch: string
|
||||||
): Promise< { deletionCommitHash: string; prNumber: number } > => {
|
): Promise< { deletionCommitHash: string; prNumber: number } > => {
|
||||||
const { owner, name, version } = options;
|
const { owner, name, version, commitDirectToBase } = options;
|
||||||
try {
|
try {
|
||||||
// Do a full checkout so that we can find the correct PR numbers for changelog entries.
|
// Do a full checkout so that we can find the correct PR numbers for changelog entries.
|
||||||
await checkoutRemoteBranch( tmpRepoPath, releaseBranch, false );
|
await checkoutRemoteBranch( tmpRepoPath, releaseBranch, false );
|
||||||
|
@ -100,10 +104,12 @@ export const updateReleaseBranchChangelogs = async (
|
||||||
const branch = `update/${ version }-changelog`;
|
const branch = `update/${ version }-changelog`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if ( ! commitDirectToBase ) {
|
||||||
await git.checkout( {
|
await git.checkout( {
|
||||||
'-b': null,
|
'-b': null,
|
||||||
[ branch ]: null,
|
[ branch ]: null,
|
||||||
} );
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
Logger.notice( `Running the changelog script in ${ tmpRepoPath }` );
|
Logger.notice( `Running the changelog script in ${ tmpRepoPath }` );
|
||||||
execSync(
|
execSync(
|
||||||
|
@ -131,9 +137,18 @@ export const updateReleaseBranchChangelogs = async (
|
||||||
await git.commit(
|
await git.commit(
|
||||||
`Update the readme files for the ${ version } release`
|
`Update the readme files for the ${ version } release`
|
||||||
);
|
);
|
||||||
await git.push( 'origin', branch );
|
await git.push( 'origin', commitDirectToBase ? releaseBranch : branch );
|
||||||
await git.checkout( '.' );
|
await git.checkout( '.' );
|
||||||
|
|
||||||
|
if ( commitDirectToBase ) {
|
||||||
|
Logger.notice(
|
||||||
|
`Changelog update was committed directly to ${ releaseBranch }`
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
deletionCommitHash: deletionCommitHash.trim(),
|
||||||
|
prNumber: -1,
|
||||||
|
};
|
||||||
|
}
|
||||||
Logger.notice( `Creating PR for ${ branch }` );
|
Logger.notice( `Creating PR for ${ branch }` );
|
||||||
const pullRequest = await createPullRequest( {
|
const pullRequest = await createPullRequest( {
|
||||||
owner,
|
owner,
|
||||||
|
@ -194,7 +209,9 @@ export const updateTrunkChangelog = async (
|
||||||
owner,
|
owner,
|
||||||
name,
|
name,
|
||||||
title: `Release: Remove ${ version } change files`,
|
title: `Release: Remove ${ version } change files`,
|
||||||
body: `This pull request was automatically generated during the code freeze to remove the changefiles from ${ version } that are compiled into the \`${ releaseBranch }\` branch via #${ prNumber }`,
|
body: `This pull request was automatically generated during the code freeze to remove the changefiles from ${ version } that are compiled into the \`${ releaseBranch }\` ${
|
||||||
|
prNumber > 0 ? `branch via #${ prNumber }` : ''
|
||||||
|
}`,
|
||||||
head: branch,
|
head: branch,
|
||||||
base: 'trunk',
|
base: 'trunk',
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export type Options = {
|
export type Options = {
|
||||||
owner: string;
|
owner?: string;
|
||||||
name: string;
|
name?: string;
|
||||||
version: string;
|
version: string;
|
||||||
devRepoPath?: string;
|
devRepoPath?: string;
|
||||||
override: string;
|
commitDirectToBase?: boolean;
|
||||||
|
override?: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { setOutput } from '@actions/core';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../core/logger';
|
||||||
|
import { isGithubCI } from '../../../core/environment';
|
||||||
|
import {
|
||||||
|
getToday,
|
||||||
|
getMonthlyCycle,
|
||||||
|
getAcceleratedCycle,
|
||||||
|
getVersionsBetween,
|
||||||
|
} from './lib/index';
|
||||||
|
|
||||||
|
const getRange = ( override, between ) => {
|
||||||
|
if ( isGithubCI() ) {
|
||||||
|
Logger.error(
|
||||||
|
'-b, --between option is not compatible with GitHub CI Output.'
|
||||||
|
);
|
||||||
|
process.exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = getToday( override );
|
||||||
|
const end = getToday( between );
|
||||||
|
const versions = getVersionsBetween( today, end );
|
||||||
|
|
||||||
|
Logger.notice(
|
||||||
|
chalk.greenBright.bold(
|
||||||
|
`Releases Between ${ today.toFormat( 'DDDD' ) } and ${ end.toFormat(
|
||||||
|
'DDDD'
|
||||||
|
) }\n`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger.table(
|
||||||
|
[ 'Version', 'Development Begins', 'Freeze', 'Release' ],
|
||||||
|
versions.map( ( v ) =>
|
||||||
|
Object.values( v ).map( ( d: DateTime | string ) =>
|
||||||
|
typeof d.toFormat === 'function'
|
||||||
|
? d.toFormat( 'EEE, MMM dd, yyyy' )
|
||||||
|
: d
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
process.exit( 0 );
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getVersionCommand = new Command( 'get-version' )
|
||||||
|
.description( 'Get the release calendar for a given date' )
|
||||||
|
.option(
|
||||||
|
'-o, --override <override>',
|
||||||
|
"Time Override: The time to use in checking whether the action should run (default: 'now').",
|
||||||
|
'now'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-b, --between <between>',
|
||||||
|
'When provided, instead of showing a single day, will show a releases in the range of <override> to <end>.'
|
||||||
|
)
|
||||||
|
.action( ( { override, between } ) => {
|
||||||
|
if ( between ) {
|
||||||
|
return getRange( override, between );
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = getToday( override );
|
||||||
|
const acceleratedRelease = getAcceleratedCycle( today, false );
|
||||||
|
const acceleratedDevelopment = getAcceleratedCycle( today );
|
||||||
|
const monthlyRelease = getMonthlyCycle( today, false );
|
||||||
|
const monthlyDevelopment = getMonthlyCycle( today );
|
||||||
|
|
||||||
|
// Generate human-friendly output.
|
||||||
|
Logger.notice(
|
||||||
|
chalk.greenBright.bold(
|
||||||
|
`Release Calendar for ${ today.toFormat( 'DDDD' ) }\n`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const table = [];
|
||||||
|
// We're not in a release cycle on Wednesday.
|
||||||
|
if ( today.get( 'weekday' ) !== 3 ) {
|
||||||
|
table.push( [
|
||||||
|
`${ chalk.red( 'Accelerated Release Cycle' ) }`,
|
||||||
|
acceleratedRelease.version,
|
||||||
|
acceleratedRelease.begin.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
acceleratedRelease.freeze.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
acceleratedRelease.release.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
table.push( [
|
||||||
|
`${ chalk.red( 'Accelerated Development Cycle' ) }`,
|
||||||
|
acceleratedDevelopment.version,
|
||||||
|
acceleratedDevelopment.begin.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
acceleratedDevelopment.freeze.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
acceleratedDevelopment.release.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
] );
|
||||||
|
// We're only in a release cycle if it is after the freeze day.
|
||||||
|
if ( today > monthlyRelease.freeze ) {
|
||||||
|
table.push( [
|
||||||
|
`${ chalk.red( 'Monthly Release Cycle' ) }`,
|
||||||
|
monthlyRelease.version,
|
||||||
|
monthlyRelease.begin.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
monthlyRelease.freeze.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
monthlyRelease.release.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
table.push( [
|
||||||
|
`${ chalk.red( 'Monthly Development Cycle' ) }`,
|
||||||
|
monthlyDevelopment.version,
|
||||||
|
monthlyDevelopment.begin.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
monthlyDevelopment.freeze.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
monthlyDevelopment.release.toFormat( 'EEE, MMM dd, yyyy' ),
|
||||||
|
] );
|
||||||
|
Logger.table(
|
||||||
|
[ '', 'Version', 'Development Begins', 'Freeze', 'Release' ],
|
||||||
|
table
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( isGithubCI() ) {
|
||||||
|
// For the machines.
|
||||||
|
const isTodayAcceleratedFreeze = today.get( 'weekday' ) === 4;
|
||||||
|
const isTodayMonthlyFreeze = +today === +monthlyDevelopment.begin;
|
||||||
|
const monthlyVersionXY = monthlyRelease.version.substr(
|
||||||
|
0,
|
||||||
|
monthlyRelease.version.lastIndexOf( '.' )
|
||||||
|
);
|
||||||
|
setOutput(
|
||||||
|
'isTodayAcceleratedFreeze',
|
||||||
|
isTodayAcceleratedFreeze ? 'yes' : 'no'
|
||||||
|
);
|
||||||
|
setOutput(
|
||||||
|
'isTodayMonthlyFreeze',
|
||||||
|
isTodayMonthlyFreeze ? 'yes' : 'no'
|
||||||
|
);
|
||||||
|
setOutput( 'acceleratedVersion', acceleratedRelease.version );
|
||||||
|
setOutput( 'monthlyVersion', monthlyRelease.version );
|
||||||
|
setOutput( 'monthlyVersionXY', monthlyVersionXY );
|
||||||
|
setOutput(
|
||||||
|
'releasesFrozenToday',
|
||||||
|
JSON.stringify(
|
||||||
|
Object.values( {
|
||||||
|
...( isTodayMonthlyFreeze && {
|
||||||
|
monthlyVersion: `${ monthlyRelease.version } (Monthly)`,
|
||||||
|
} ),
|
||||||
|
...( isTodayAcceleratedFreeze && {
|
||||||
|
aVersion: `${ acceleratedRelease.version } (AF)`,
|
||||||
|
} ),
|
||||||
|
} )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setOutput(
|
||||||
|
'acceleratedBranch',
|
||||||
|
`release/${ acceleratedRelease.version }`
|
||||||
|
);
|
||||||
|
setOutput( 'monthlyBranch', `release/${ monthlyVersionXY }` );
|
||||||
|
setOutput( 'monthlyMilestone', monthlyDevelopment.version );
|
||||||
|
setOutput(
|
||||||
|
'acceleratedReleaseDate',
|
||||||
|
acceleratedDevelopment.release.toISODate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit( 0 );
|
||||||
|
} );
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
export const DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE = 19;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a DateTime object of now or the override time when specified. DateTime is normalized to start of day.
|
||||||
|
*
|
||||||
|
* @param {string} now The time to use in checking if today is the day of the code freeze. Default to now. Supports ISO formatted dates or 'now'.
|
||||||
|
*
|
||||||
|
* @return {DateTime} The DateTime object of now or the override time when specified.
|
||||||
|
*/
|
||||||
|
export const getToday = ( now = 'now' ): DateTime => {
|
||||||
|
const today =
|
||||||
|
now === 'now'
|
||||||
|
? DateTime.now().setZone( 'utc' )
|
||||||
|
: DateTime.fromISO( now, { zone: 'utc' } );
|
||||||
|
if ( isNaN( today.toMillis() ) ) {
|
||||||
|
throw new Error(
|
||||||
|
'Invalid date: Check the override parameter (-o, --override) is a correct ISO formatted string or "now"'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return today.set( { hour: 0, minute: 0, second: 0, millisecond: 0 } );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the second Tuesday of the month, given a DateTime.
|
||||||
|
*
|
||||||
|
* @param {DateTime} when A DateTime object.
|
||||||
|
*
|
||||||
|
* @return {DateTime} The second Tuesday of the month contained in the input.
|
||||||
|
*/
|
||||||
|
export const getSecondTuesday = ( when: DateTime ): DateTime => {
|
||||||
|
const year = when.get( 'year' );
|
||||||
|
const month = when.get( 'month' );
|
||||||
|
const firstDayOfMonth = DateTime.utc( year, month, 1 );
|
||||||
|
const dayOfWeek = firstDayOfMonth.get( 'weekday' );
|
||||||
|
const secondTuesday = dayOfWeek <= 2 ? 10 - dayOfWeek : 17 - dayOfWeek;
|
||||||
|
return DateTime.utc( year, month, secondTuesday );
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMonthlyCycle = ( when: DateTime, development = true ) => {
|
||||||
|
// July 12, 2023 is the start-point for 8.0.0, all versions follow that starting point.
|
||||||
|
const startTime = DateTime.fromObject(
|
||||||
|
{
|
||||||
|
year: 2023,
|
||||||
|
month: 7,
|
||||||
|
day: 12,
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
},
|
||||||
|
{ zone: 'UTC' }
|
||||||
|
);
|
||||||
|
const currentMonthRelease = getSecondTuesday( when );
|
||||||
|
const nextMonthRelease = getSecondTuesday(
|
||||||
|
currentMonthRelease.plus( { months: 1 } )
|
||||||
|
);
|
||||||
|
const release =
|
||||||
|
when <= currentMonthRelease ? currentMonthRelease : nextMonthRelease;
|
||||||
|
const previousRelease = getSecondTuesday(
|
||||||
|
release.minus( { days: DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE + 2 } )
|
||||||
|
);
|
||||||
|
const nextRelease = getSecondTuesday( release.plus( { months: 1 } ) );
|
||||||
|
const freeze = release.minus( {
|
||||||
|
days: DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE + 1,
|
||||||
|
} );
|
||||||
|
const monthNumber =
|
||||||
|
( previousRelease.get( 'year' ) - startTime.get( 'year' ) ) * 12 +
|
||||||
|
previousRelease.get( 'month' ) -
|
||||||
|
startTime.get( 'month' );
|
||||||
|
const version = ( ( 80 + monthNumber ) / 10 ).toFixed( 1 ) + '.0';
|
||||||
|
|
||||||
|
if ( development ) {
|
||||||
|
if ( when > freeze ) {
|
||||||
|
return getMonthlyCycle( nextRelease, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const begin = previousRelease.minus( {
|
||||||
|
days: DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE,
|
||||||
|
} );
|
||||||
|
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
begin,
|
||||||
|
freeze,
|
||||||
|
release,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get version and all dates / related to an accelerated cycle.
|
||||||
|
*
|
||||||
|
* @param {DateTime} when A DateTime object.
|
||||||
|
* @param {boolean} development When true, the active development cycle will be returned, otherwise the active release cycle.
|
||||||
|
* @return {Object} An object containing version and dates for a release.
|
||||||
|
*/
|
||||||
|
export const getAcceleratedCycle = ( when: DateTime, development = true ) => {
|
||||||
|
if ( ! development ) {
|
||||||
|
when = when.minus( { week: 1 } );
|
||||||
|
}
|
||||||
|
const dayOfWeek = when.get( 'weekday' );
|
||||||
|
const daysTilWednesday = dayOfWeek < 4 ? 3 - dayOfWeek : 10 - dayOfWeek;
|
||||||
|
const freeze = when.plus( { days: daysTilWednesday } );
|
||||||
|
const lastAccelerated = freeze.minus( { days: 1 } );
|
||||||
|
const release = freeze.plus( { days: 6 } );
|
||||||
|
const begin = freeze.minus( { days: 6 } );
|
||||||
|
const currentMonthRelease = getSecondTuesday( lastAccelerated );
|
||||||
|
const nextMonthRelease = getSecondTuesday(
|
||||||
|
currentMonthRelease.plus( { months: 1 } )
|
||||||
|
);
|
||||||
|
const monthlyRelease =
|
||||||
|
freeze <= currentMonthRelease ? currentMonthRelease : nextMonthRelease;
|
||||||
|
const monthlyCycle = getMonthlyCycle( monthlyRelease, false );
|
||||||
|
const previousMonthlyRelease = getSecondTuesday(
|
||||||
|
monthlyRelease.minus( { days: 28 } )
|
||||||
|
);
|
||||||
|
|
||||||
|
const aVersion =
|
||||||
|
10 *
|
||||||
|
( lastAccelerated.diff( previousMonthlyRelease, 'weeks' ).toObject()
|
||||||
|
.weeks +
|
||||||
|
1 );
|
||||||
|
const version = `${ monthlyCycle.version }.${ aVersion }`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
begin,
|
||||||
|
freeze,
|
||||||
|
release,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getVersionsBetween = ( start: DateTime, end: DateTime ) => {
|
||||||
|
if ( start > end ) {
|
||||||
|
return getVersionsBetween( end, start );
|
||||||
|
}
|
||||||
|
const versions = {};
|
||||||
|
for ( let i = start; i < end; i = i.plus( { days: 28 } ) ) {
|
||||||
|
const monthly = getMonthlyCycle( i, false );
|
||||||
|
versions[ monthly.version ] = monthly;
|
||||||
|
}
|
||||||
|
for ( let i = start; i < end; i = i.plus( { days: 7 } ) ) {
|
||||||
|
const accelerated = getAcceleratedCycle( i, false );
|
||||||
|
versions[ accelerated.version ] = accelerated;
|
||||||
|
}
|
||||||
|
return Object.values( versions );
|
||||||
|
};
|
|
@ -6,18 +6,20 @@ import { Command } from '@commander-js/extra-typings';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { verifyDayCommand } from './verify-day';
|
import { getVersionCommand } from './get-version';
|
||||||
import { milestoneCommand } from './milestone';
|
import { milestoneCommand } from './milestone';
|
||||||
import { branchCommand } from './branch';
|
import { branchCommand } from './branch';
|
||||||
import { versionBumpCommand } from './version-bump';
|
import { versionBumpCommand } from './version-bump';
|
||||||
import { changelogCommand } from './changelog';
|
import { changelogCommand } from './changelog';
|
||||||
|
import { acceleratedPrepCommand } from './accelerated-prep';
|
||||||
|
|
||||||
const program = new Command( 'code-freeze' )
|
const program = new Command( 'code-freeze' )
|
||||||
.description( 'Code freeze utilities' )
|
.description( 'Code freeze utilities' )
|
||||||
.addCommand( verifyDayCommand )
|
.addCommand( getVersionCommand )
|
||||||
.addCommand( milestoneCommand )
|
.addCommand( milestoneCommand )
|
||||||
.addCommand( branchCommand )
|
.addCommand( branchCommand )
|
||||||
.addCommand( versionBumpCommand )
|
.addCommand( versionBumpCommand )
|
||||||
.addCommand( changelogCommand );
|
.addCommand( changelogCommand )
|
||||||
|
.addCommand( acceleratedPrepCommand );
|
||||||
|
|
||||||
export default program;
|
export default program;
|
||||||
|
|
|
@ -9,10 +9,8 @@ import ora from 'ora';
|
||||||
*/
|
*/
|
||||||
import { getLatestGithubReleaseVersion } from '../../../core/github/repo';
|
import { getLatestGithubReleaseVersion } from '../../../core/github/repo';
|
||||||
import { octokitWithAuth } from '../../../core/github/api';
|
import { octokitWithAuth } from '../../../core/github/api';
|
||||||
import { setGithubMilestoneOutputs } from './utils';
|
|
||||||
import { WPIncrement } from '../../../core/version';
|
import { WPIncrement } from '../../../core/version';
|
||||||
import { Logger } from '../../../core/logger';
|
import { Logger } from '../../../core/logger';
|
||||||
import { isGithubCI } from '../../../core/environment';
|
|
||||||
|
|
||||||
export const milestoneCommand = new Command( 'milestone' )
|
export const milestoneCommand = new Command( 'milestone' )
|
||||||
.description( 'Create a milestone' )
|
.description( 'Create a milestone' )
|
||||||
|
@ -33,14 +31,6 @@ export const milestoneCommand = new Command( 'milestone' )
|
||||||
)
|
)
|
||||||
.action( async ( options ) => {
|
.action( async ( options ) => {
|
||||||
const { owner, name, dryRun, milestone } = options;
|
const { owner, name, dryRun, milestone } = options;
|
||||||
const isGithub = isGithubCI();
|
|
||||||
|
|
||||||
if ( milestone && isGithub ) {
|
|
||||||
Logger.error(
|
|
||||||
"You can't manually supply a milestone using Github mode. Please use the CLI locally to add a milestone."
|
|
||||||
);
|
|
||||||
process.exit( 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextMilestone;
|
let nextMilestone;
|
||||||
let nextReleaseVersion;
|
let nextReleaseVersion;
|
||||||
|
@ -105,12 +95,6 @@ export const milestoneCommand = new Command( 'milestone' )
|
||||||
Logger.notice(
|
Logger.notice(
|
||||||
`Milestone ${ nextMilestone } already exists in ${ owner }/${ name }`
|
`Milestone ${ nextMilestone } already exists in ${ owner }/${ name }`
|
||||||
);
|
);
|
||||||
if ( isGithub ) {
|
|
||||||
setGithubMilestoneOutputs(
|
|
||||||
nextReleaseVersion,
|
|
||||||
nextMilestone
|
|
||||||
);
|
|
||||||
}
|
|
||||||
process.exit( 0 );
|
process.exit( 0 );
|
||||||
} else {
|
} else {
|
||||||
milestoneSpinner.fail();
|
milestoneSpinner.fail();
|
||||||
|
@ -123,9 +107,7 @@ export const milestoneCommand = new Command( 'milestone' )
|
||||||
}
|
}
|
||||||
|
|
||||||
milestoneSpinner.succeed();
|
milestoneSpinner.succeed();
|
||||||
if ( isGithub ) {
|
|
||||||
setGithubMilestoneOutputs( nextReleaseVersion, nextMilestone );
|
|
||||||
}
|
|
||||||
Logger.notice(
|
Logger.notice(
|
||||||
`Successfully created milestone ${ nextMilestone } in ${ owner }/${ name }`
|
`Successfully created milestone ${ nextMilestone } in ${ owner }/${ name }`
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { setOutput } from '@actions/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { getMajorMinor } from '../../../core/version';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Github outputs.
|
|
||||||
*
|
|
||||||
* @param {string} nextReleaseVersion Next release version
|
|
||||||
* @param {string} nextMilestone Next milestone
|
|
||||||
*/
|
|
||||||
export const setGithubMilestoneOutputs = (
|
|
||||||
nextReleaseVersion: string,
|
|
||||||
nextMilestone: string
|
|
||||||
): void => {
|
|
||||||
setOutput( 'nextReleaseVersion', getMajorMinor( nextReleaseVersion ) );
|
|
||||||
setOutput( 'nextDevelopmentVersion', getMajorMinor( nextMilestone ) );
|
|
||||||
};
|
|
|
@ -1,48 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { Command } from '@commander-js/extra-typings';
|
|
||||||
import { setOutput } from '@actions/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
isTodayCodeFreezeDay,
|
|
||||||
DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE,
|
|
||||||
getToday,
|
|
||||||
getFutureDate,
|
|
||||||
} from './utils';
|
|
||||||
import { Logger } from '../../../core/logger';
|
|
||||||
import { isGithubCI } from '../../../core/environment';
|
|
||||||
|
|
||||||
export const verifyDayCommand = new Command( 'verify-day' )
|
|
||||||
.description( 'Verify if today is the code freeze day' )
|
|
||||||
.option(
|
|
||||||
'-o, --override <override>',
|
|
||||||
"Time Override: The time to use in checking whether the action should run (default: 'now').",
|
|
||||||
'now'
|
|
||||||
)
|
|
||||||
.action( ( { override } ) => {
|
|
||||||
const today = getToday( override );
|
|
||||||
const futureDate = getFutureDate( today );
|
|
||||||
Logger.warn( "Today's timestamp UTC is: " + today.toUTCString() );
|
|
||||||
Logger.warn(
|
|
||||||
`Checking to see if ${ DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE } days from today is the second Tuesday of the month.`
|
|
||||||
);
|
|
||||||
const isCodeFreezeDay = isTodayCodeFreezeDay( override );
|
|
||||||
Logger.notice(
|
|
||||||
`${ futureDate.toUTCString() } ${
|
|
||||||
isCodeFreezeDay ? 'is' : 'is not'
|
|
||||||
} release day.`
|
|
||||||
);
|
|
||||||
Logger.notice(
|
|
||||||
`Today is ${ isCodeFreezeDay ? 'indeed' : 'not' } code freeze day.`
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( isGithubCI() ) {
|
|
||||||
setOutput( 'freeze', isCodeFreezeDay.toString() );
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit( 0 );
|
|
||||||
} );
|
|
|
@ -1,36 +0,0 @@
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { isTodayCodeFreezeDay } from '../index';
|
|
||||||
|
|
||||||
describe( 'isTodayCodeFreezeDay', () => {
|
|
||||||
it( 'should return false when given a day not 22 days before release', () => {
|
|
||||||
const JUNE_5_2023 = '2023-06-05T00:00:00.000Z';
|
|
||||||
const JUNE_12_2023 = '2023-06-12T00:00:00.000Z';
|
|
||||||
const JUNE_26_2023 = '2023-06-26T00:00:00.000Z';
|
|
||||||
const AUG_10_2023 = '2023-08-10T00:00:00.000Z';
|
|
||||||
const AUG_17_2023 = '2023-08-17T00:00:00.000Z';
|
|
||||||
const AUG_24_2023 = '2023-08-24T00:00:00.000Z';
|
|
||||||
|
|
||||||
expect( isTodayCodeFreezeDay( JUNE_5_2023 ) ).toBeFalsy();
|
|
||||||
expect( isTodayCodeFreezeDay( JUNE_12_2023 ) ).toBeFalsy();
|
|
||||||
expect( isTodayCodeFreezeDay( JUNE_26_2023 ) ).toBeFalsy();
|
|
||||||
expect( isTodayCodeFreezeDay( AUG_10_2023 ) ).toBeFalsy();
|
|
||||||
expect( isTodayCodeFreezeDay( AUG_17_2023 ) ).toBeFalsy();
|
|
||||||
expect( isTodayCodeFreezeDay( AUG_24_2023 ) ).toBeFalsy();
|
|
||||||
} );
|
|
||||||
|
|
||||||
it( 'should return true when given a day 22 days before release', () => {
|
|
||||||
const JUNE_19_2023 = '2023-06-19T00:00:00.000Z';
|
|
||||||
const JULY_17_2023 = '2023-07-17T00:00:00.000Z';
|
|
||||||
const AUGUST_21_2023 = '2023-08-21T00:00:00.000Z';
|
|
||||||
|
|
||||||
expect( isTodayCodeFreezeDay( JUNE_19_2023 ) ).toBeTruthy();
|
|
||||||
expect( isTodayCodeFreezeDay( JULY_17_2023 ) ).toBeTruthy();
|
|
||||||
expect( isTodayCodeFreezeDay( AUGUST_21_2023 ) ).toBeTruthy();
|
|
||||||
} );
|
|
||||||
|
|
||||||
it( 'should error out when passed an invalid date', () => {
|
|
||||||
expect( () => isTodayCodeFreezeDay( 'invalid date' ) ).toThrow();
|
|
||||||
} );
|
|
||||||
} );
|
|
|
@ -1,46 +0,0 @@
|
||||||
const MILLIS_IN_A_DAY = 24 * 60 * 60 * 1000;
|
|
||||||
export const DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE = 22;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a Date object of now or the override time when specified.
|
|
||||||
*
|
|
||||||
* @param {string} now The time to use in checking if today is the day of the code freeze. Default to now.
|
|
||||||
* @return {Date} The Date object of now or the override time when specified.
|
|
||||||
*/
|
|
||||||
export const getToday = ( now = 'now' ): Date => {
|
|
||||||
const today = now === 'now' ? new Date() : new Date( now );
|
|
||||||
if ( isNaN( today.getTime() ) ) {
|
|
||||||
throw new Error(
|
|
||||||
'Invalid date: Check the override parameter (-o, --override) is a correct Date string'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return today;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a future date from today to see if its the release day.
|
|
||||||
*
|
|
||||||
* @param {string} today The time to use in checking if today is the day of the code freeze. Default to now.
|
|
||||||
* @return {Date} The Date object of the future date.
|
|
||||||
*/
|
|
||||||
export const getFutureDate = ( today: Date ) => {
|
|
||||||
return new Date(
|
|
||||||
today.getTime() + DAYS_BETWEEN_CODE_FREEZE_AND_RELEASE * MILLIS_IN_A_DAY
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Determines if today is the day of the code freeze.
|
|
||||||
*
|
|
||||||
* @param {string} now The time to use in checking if today is the day of the code freeze. Default to now.
|
|
||||||
* @return {boolean} true if today is the day of the code freeze.
|
|
||||||
*/
|
|
||||||
export const isTodayCodeFreezeDay = ( now: string ) => {
|
|
||||||
const today = getToday( now );
|
|
||||||
const futureDate = getFutureDate( today );
|
|
||||||
const month = futureDate.getUTCMonth();
|
|
||||||
const year = futureDate.getUTCFullYear();
|
|
||||||
const firstDayOfMonth = new Date( Date.UTC( year, month, 1 ) );
|
|
||||||
const dayOfWeek = firstDayOfMonth.getUTCDay();
|
|
||||||
const secondTuesday = dayOfWeek <= 2 ? 10 - dayOfWeek : 17 - dayOfWeek;
|
|
||||||
return futureDate.getUTCDate() === secondTuesday;
|
|
||||||
};
|
|
|
@ -4,10 +4,6 @@
|
||||||
import { Command } from '@commander-js/extra-typings';
|
import { Command } from '@commander-js/extra-typings';
|
||||||
import { ErrorCode, WebClient } from '@slack/web-api';
|
import { ErrorCode, WebClient } from '@slack/web-api';
|
||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
|
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +27,22 @@ export const slackFileCommand = new Command( 'file' )
|
||||||
'--dont-fail',
|
'--dont-fail',
|
||||||
'Do not fail the command if a message fails to send to any channel.'
|
'Do not fail the command if a message fails to send to any channel.'
|
||||||
)
|
)
|
||||||
.action( async ( token, text, filePath, channels, { dontFail } ) => {
|
.option(
|
||||||
|
'--reply-ts <replyTs>',
|
||||||
|
'Reply to the message with the corresponding ts'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'--filename <filename>',
|
||||||
|
'If provided, the filename that will be used for the file on Slack.'
|
||||||
|
)
|
||||||
|
.action(
|
||||||
|
async (
|
||||||
|
token,
|
||||||
|
text,
|
||||||
|
filePath,
|
||||||
|
channels,
|
||||||
|
{ dontFail, replyTs, filename }
|
||||||
|
) => {
|
||||||
Logger.startTask(
|
Logger.startTask(
|
||||||
`Attempting to send message to Slack for channels: ${ channels.join(
|
`Attempting to send message to Slack for channels: ${ channels.join(
|
||||||
','
|
','
|
||||||
|
@ -51,13 +62,16 @@ export const slackFileCommand = new Command( 'file' )
|
||||||
|
|
||||||
for ( const channel of channels ) {
|
for ( const channel of channels ) {
|
||||||
try {
|
try {
|
||||||
await client.files.uploadV2( {
|
const requestOptions = {
|
||||||
file: filePath,
|
file: filePath,
|
||||||
filename: basename( filePath ),
|
filename: filename ? filename : basename( filePath ),
|
||||||
channel_id: channel,
|
channel_id: channel,
|
||||||
initial_comment: text.replace( /\\n/g, '\n' ),
|
initial_comment: text.replace( /\\n/g, '\n' ),
|
||||||
request_file_info: false,
|
request_file_info: false,
|
||||||
} );
|
thread_ts: replyTs ? replyTs : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.files.uploadV2( requestOptions );
|
||||||
|
|
||||||
Logger.notice(
|
Logger.notice(
|
||||||
`Successfully uploaded ${ filePath } to channel: ${ channel }`
|
`Successfully uploaded ${ filePath } to channel: ${ channel }`
|
||||||
|
@ -80,4 +94,5 @@ export const slackFileCommand = new Command( 'file' )
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.endTask();
|
Logger.endTask();
|
||||||
} );
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -2,16 +2,20 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { Command } from '@commander-js/extra-typings';
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
import { setOutput } from '@actions/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { Logger } from '../../../core/logger';
|
import { Logger } from '../../../core/logger';
|
||||||
import { requestAsync } from '../../../core/util';
|
import { requestAsync } from '../../../core/util';
|
||||||
|
import { isGithubCI } from '../../../core/environment';
|
||||||
|
|
||||||
type SlackResponse = {
|
type SlackResponse = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
channel?: string;
|
||||||
|
ts?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const slackMessageCommand = new Command( 'message' )
|
export const slackMessageCommand = new Command( 'message' )
|
||||||
|
@ -72,6 +76,9 @@ export const slackMessageCommand = new Command( 'message' )
|
||||||
Logger.notice(
|
Logger.notice(
|
||||||
`Slack message sent successfully to channel: ${ channel }`
|
`Slack message sent successfully to channel: ${ channel }`
|
||||||
);
|
);
|
||||||
|
if ( isGithubCI() ) {
|
||||||
|
setOutput( 'ts', response.ts );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch ( e: unknown ) {
|
} catch ( e: unknown ) {
|
||||||
Logger.error( e, shouldFail );
|
Logger.error( e, shouldFail );
|
||||||
|
|
Loading…
Reference in New Issue