Add monorepo util to notify slack, add improvements to calling utils and type clean up. (#38185)
This commit is contained in:
parent
b581db2a7e
commit
95ac08739b
|
@ -30,7 +30,7 @@
|
||||||
"create-extension": "node ./tools/create-extension/index.js",
|
"create-extension": "node ./tools/create-extension/index.js",
|
||||||
"cherry-pick": "node ./tools/cherry-pick/bin/run",
|
"cherry-pick": "node ./tools/cherry-pick/bin/run",
|
||||||
"sync-dependencies": "pnpm exec syncpack -- fix-mismatches",
|
"sync-dependencies": "pnpm exec syncpack -- fix-mismatches",
|
||||||
"utils": "./tools/monorepo-utils/bin/run"
|
"utils": "node ./tools/monorepo-utils/dist/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-env": "^7.20.2",
|
"@babel/preset-env": "^7.20.2",
|
||||||
|
|
|
@ -3242,8 +3242,8 @@ importers:
|
||||||
specifier: ^1.10.0
|
specifier: ^1.10.0
|
||||||
version: 1.10.0
|
version: 1.10.0
|
||||||
'@commander-js/extra-typings':
|
'@commander-js/extra-typings':
|
||||||
specifier: ^0.1.0
|
specifier: ^10.0.3
|
||||||
version: 0.1.0(commander@9.4.0)
|
version: 10.0.3(commander@10.0.1)
|
||||||
'@octokit/graphql':
|
'@octokit/graphql':
|
||||||
specifier: 4.8.0
|
specifier: 4.8.0
|
||||||
version: 4.8.0
|
version: 4.8.0
|
||||||
|
@ -3257,8 +3257,8 @@ importers:
|
||||||
specifier: ^4.1.2
|
specifier: ^4.1.2
|
||||||
version: 4.1.2
|
version: 4.1.2
|
||||||
commander:
|
commander:
|
||||||
specifier: ^9.4.0
|
specifier: ^10.0.1
|
||||||
version: 9.4.0
|
version: 10.0.1
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^10.0.0
|
specifier: ^10.0.0
|
||||||
version: 10.0.0
|
version: 10.0.0
|
||||||
|
@ -3871,7 +3871,7 @@ packages:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@jridgewell/trace-mapping': 0.3.16
|
'@jridgewell/trace-mapping': 0.3.17
|
||||||
commander: 4.1.1
|
commander: 4.1.1
|
||||||
convert-source-map: 1.8.0
|
convert-source-map: 1.8.0
|
||||||
fs-readdir-recursive: 1.1.0
|
fs-readdir-recursive: 1.1.0
|
||||||
|
@ -8720,9 +8720,9 @@ packages:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@babel/helper-annotate-as-pure': 7.16.7
|
'@babel/helper-annotate-as-pure': 7.16.7
|
||||||
'@babel/helper-module-imports': 7.16.7
|
'@babel/helper-module-imports': 7.16.7
|
||||||
'@babel/helper-plugin-utils': 7.20.2
|
'@babel/helper-plugin-utils': 7.18.9
|
||||||
'@babel/plugin-syntax-jsx': 7.16.7(@babel/core@7.21.3)
|
'@babel/plugin-syntax-jsx': 7.16.7(@babel/core@7.21.3)
|
||||||
'@babel/types': 7.21.3
|
'@babel/types': 7.17.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.12.9):
|
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.12.9):
|
||||||
|
@ -8951,8 +8951,8 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@babel/helper-module-imports': 7.16.0
|
'@babel/helper-module-imports': 7.16.0
|
||||||
'@babel/helper-plugin-utils': 7.14.5
|
'@babel/helper-plugin-utils': 7.20.2
|
||||||
babel-plugin-polyfill-corejs2: 0.3.0(@babel/core@7.21.3)
|
babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.3)
|
||||||
babel-plugin-polyfill-corejs3: 0.4.0(@babel/core@7.21.3)
|
babel-plugin-polyfill-corejs3: 0.4.0(@babel/core@7.21.3)
|
||||||
babel-plugin-polyfill-regenerator: 0.3.0(@babel/core@7.21.3)
|
babel-plugin-polyfill-regenerator: 0.3.0(@babel/core@7.21.3)
|
||||||
semver: 6.3.0
|
semver: 6.3.0
|
||||||
|
@ -10461,6 +10461,14 @@ packages:
|
||||||
commander: 9.4.0
|
commander: 9.4.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@commander-js/extra-typings@10.0.3(commander@10.0.1):
|
||||||
|
resolution: {integrity: sha512-OIw28QV/GlP8k0B5CJTRsl8IyNvd0R8C8rfo54Yz9P388vCNDgdNrFlKxZTGqps+5j6lSw3Ss9JTQwcur1w1oA==}
|
||||||
|
peerDependencies:
|
||||||
|
commander: 10.0.x
|
||||||
|
dependencies:
|
||||||
|
commander: 10.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cspotcode/source-map-support@0.8.1:
|
/@cspotcode/source-map-support@0.8.1:
|
||||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -11961,6 +11969,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.0
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@jridgewell/trace-mapping@0.3.17:
|
/@jridgewell/trace-mapping@0.3.17:
|
||||||
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
||||||
|
@ -20854,8 +20863,8 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.21.4
|
browserslist: 4.20.2
|
||||||
caniuse-lite: 1.0.30001418
|
caniuse-lite: 1.0.30001352
|
||||||
fraction.js: 4.2.0
|
fraction.js: 4.2.0
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
|
@ -22398,7 +22407,6 @@ packages:
|
||||||
escalade: 3.1.1
|
escalade: 3.1.1
|
||||||
node-releases: 2.0.6
|
node-releases: 2.0.6
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/browserslist@4.20.4:
|
/browserslist@4.20.4:
|
||||||
resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
|
resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
|
||||||
|
@ -23443,6 +23451,11 @@ packages:
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/commander@10.0.1:
|
||||||
|
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/commander@2.1.0:
|
/commander@2.1.0:
|
||||||
resolution: {integrity: sha512-J2wnb6TKniXNOtoHS8TSrG9IOQluPrsmyAJ8oCUJOBmv+uLBCyPYAZkD2jFvw2DCzIXNnISIM01NIvr35TkBMQ==}
|
resolution: {integrity: sha512-J2wnb6TKniXNOtoHS8TSrG9IOQluPrsmyAJ8oCUJOBmv+uLBCyPYAZkD2jFvw2DCzIXNnISIM01NIvr35TkBMQ==}
|
||||||
engines: {node: '>= 0.6.x'}
|
engines: {node: '>= 0.6.x'}
|
||||||
|
|
|
@ -2,4 +2,19 @@
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
Monorepo utilities and tooling.
|
A set of CLI tools and scripts for managing the WooCommerce monorepo.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This command is built on postinstall and can be run from the root of the project.
|
||||||
|
To see a list of available commands you can run this from project root:
|
||||||
|
|
||||||
|
```
|
||||||
|
pnpm utils
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
During development you can watch for changes via `pnpm start` in this directory, this will
|
||||||
|
update the script referenced at the root package.json so you will see immediate changes as you
|
||||||
|
re-run the CLI.
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const { existsSync } = require( 'fs' );
|
|
||||||
const chalk = require( 'chalk' );
|
|
||||||
const path = require( 'path' );
|
|
||||||
|
|
||||||
const nodeModulesDirectory = path.join( __dirname, '../', 'node_modules' );
|
|
||||||
|
|
||||||
if ( ! existsSync( nodeModulesDirectory ) ) {
|
|
||||||
console.log(
|
|
||||||
chalk.red(
|
|
||||||
'The @woocommerce/monorepo-utils must be built before running the CLI.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
'run `pnpm run build` from the root of the monorepo-utils package. or `pnpm install --filter monorepo-utils` from project root.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
process.exit( 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
const { program } = require( '../dist/index' );
|
|
||||||
|
|
||||||
program.parse( process.argv );
|
|
|
@ -9,12 +9,12 @@
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@commander-js/extra-typings": "^0.1.0",
|
"@commander-js/extra-typings": "^10.0.3",
|
||||||
"@octokit/graphql": "4.8.0",
|
"@octokit/graphql": "4.8.0",
|
||||||
"@octokit/graphql-schema": "^14.1.0",
|
"@octokit/graphql-schema": "^14.1.0",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"commander": "^9.4.0",
|
"commander": "^10.0.1",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"figlet": "^1.6.0",
|
"figlet": "^1.6.0",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
|
|
|
@ -20,10 +20,12 @@ import {
|
||||||
} from '../../../core/github/repo';
|
} from '../../../core/github/repo';
|
||||||
import { WPIncrement } from '../../../core/version';
|
import { WPIncrement } from '../../../core/version';
|
||||||
import { Logger } from '../../../core/logger';
|
import { Logger } from '../../../core/logger';
|
||||||
import { Options } from './types';
|
|
||||||
import { getEnvVar } from '../../../core/environment';
|
import { getEnvVar } from '../../../core/environment';
|
||||||
|
|
||||||
const getNextReleaseBranch = async ( options: Options ) => {
|
const getNextReleaseBranch = async ( options: {
|
||||||
|
owner?: string;
|
||||||
|
name?: string;
|
||||||
|
} ) => {
|
||||||
const latestReleaseVersion = await getLatestGithubReleaseVersion( options );
|
const latestReleaseVersion = await getLatestGithubReleaseVersion( options );
|
||||||
const nextReleaseVersion = WPIncrement( latestReleaseVersion );
|
const nextReleaseVersion = WPIncrement( latestReleaseVersion );
|
||||||
const parsedNextReleaseVersion = parse( nextReleaseVersion );
|
const parsedNextReleaseVersion = parse( nextReleaseVersion );
|
||||||
|
@ -53,7 +55,7 @@ export const branchCommand = new Command( 'branch' )
|
||||||
'Branch to create the release branch from. Default: trunk',
|
'Branch to create the release branch from. Default: trunk',
|
||||||
'trunk'
|
'trunk'
|
||||||
)
|
)
|
||||||
.action( async ( options: Options ) => {
|
.action( async ( options ) => {
|
||||||
const { source, branch, owner, name, dryRun } = options;
|
const { source, branch, owner, name, dryRun } = options;
|
||||||
const isGithub = getEnvVar( 'CI' );
|
const isGithub = getEnvVar( 'CI' );
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
export type Options = {
|
|
||||||
github?: boolean;
|
|
||||||
dryRun?: boolean;
|
|
||||||
owner?: string;
|
|
||||||
name?: string;
|
|
||||||
branch?: string;
|
|
||||||
source?: string;
|
|
||||||
};
|
|
|
@ -11,7 +11,6 @@ import { getLatestGithubReleaseVersion } from '../../../core/github/repo';
|
||||||
import { octokitWithAuth } from '../../../core/github/api';
|
import { octokitWithAuth } from '../../../core/github/api';
|
||||||
import { setGithubMilestoneOutputs } from './utils';
|
import { setGithubMilestoneOutputs } from './utils';
|
||||||
import { WPIncrement } from '../../../core/version';
|
import { WPIncrement } from '../../../core/version';
|
||||||
import { Options } from './types';
|
|
||||||
import { Logger } from '../../../core/logger';
|
import { Logger } from '../../../core/logger';
|
||||||
import { getEnvVar } from '../../../core/environment';
|
import { getEnvVar } from '../../../core/environment';
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ export const milestoneCommand = new Command( 'milestone' )
|
||||||
'-m --milestone <milestone>',
|
'-m --milestone <milestone>',
|
||||||
'Milestone to create. Next milestone is gathered from Github if none is supplied'
|
'Milestone to create. Next milestone is gathered from Github if none is supplied'
|
||||||
)
|
)
|
||||||
.action( async ( options: Options ) => {
|
.action( async ( options ) => {
|
||||||
const { owner, name, dryRun, milestone } = options;
|
const { owner, name, dryRun, milestone } = options;
|
||||||
const isGithub = getEnvVar( 'CI' );
|
const isGithub = getEnvVar( 'CI' );
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
export type Options = {
|
|
||||||
github?: boolean;
|
|
||||||
dryRun?: boolean;
|
|
||||||
owner?: string;
|
|
||||||
name?: string;
|
|
||||||
milestone?: string;
|
|
||||||
};
|
|
|
@ -14,3 +14,7 @@ export const getEnvVar = ( varName: string, isRequired = false ) => {
|
||||||
|
|
||||||
return value || '';
|
return value || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isGithubCI = () => {
|
||||||
|
return process.env.GITHUB_ACTIONS === 'true';
|
||||||
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ import chalk from 'chalk';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getEnvVar } from './environment';
|
import { getEnvVar, isGithubCI } from './environment';
|
||||||
|
|
||||||
const LOGGING_LEVELS: Record< string, number > = {
|
const LOGGING_LEVELS: Record< string, number > = {
|
||||||
verbose: 3,
|
verbose: 3,
|
||||||
|
@ -28,7 +28,7 @@ export class Logger {
|
||||||
static error( err: unknown, failOnErr = true ) {
|
static error( err: unknown, failOnErr = true ) {
|
||||||
if ( Logger.loggingLevel >= LOGGING_LEVELS.error ) {
|
if ( Logger.loggingLevel >= LOGGING_LEVELS.error ) {
|
||||||
if ( err instanceof Error ) {
|
if ( err instanceof Error ) {
|
||||||
error( chalk.red( err.message ) );
|
error( chalk.red( `${ err.message }\n${ err.stack }` ) );
|
||||||
} else if ( typeof err === 'string' ) {
|
} else if ( typeof err === 'string' ) {
|
||||||
error( chalk.red( err ) );
|
error( chalk.red( err ) );
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,21 +55,26 @@ export class Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
static startTask( message: string ) {
|
static startTask( message: string ) {
|
||||||
if ( Logger.loggingLevel > LOGGING_LEVELS.silent ) {
|
if ( Logger.loggingLevel > LOGGING_LEVELS.silent && ! isGithubCI() ) {
|
||||||
const spinner = ora( chalk.green( `${ message }...` ) ).start();
|
const spinner = ora( chalk.green( `${ message }...` ) ).start();
|
||||||
Logger.lastSpinner = spinner;
|
Logger.lastSpinner = spinner;
|
||||||
|
} else if ( isGithubCI() ) {
|
||||||
|
Logger.notice( message );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static endTask() {
|
static endTask() {
|
||||||
if (
|
if (
|
||||||
Logger.loggingLevel > LOGGING_LEVELS.silent &&
|
Logger.loggingLevel > LOGGING_LEVELS.silent &&
|
||||||
Logger.lastSpinner
|
Logger.lastSpinner &&
|
||||||
|
! isGithubCI()
|
||||||
) {
|
) {
|
||||||
Logger.lastSpinner.succeed(
|
Logger.lastSpinner.succeed(
|
||||||
`${ Logger.lastSpinner.text } complete.`
|
`${ Logger.lastSpinner.text } complete.`
|
||||||
);
|
);
|
||||||
Logger.lastSpinner = null;
|
Logger.lastSpinner = null;
|
||||||
|
} else if ( isGithubCI() ) {
|
||||||
|
Logger.notice( 'Task complete.' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,48 @@
|
||||||
*/
|
*/
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
|
import { RequestOptions, request } from 'https';
|
||||||
|
import { IncomingMessage } from 'http';
|
||||||
|
|
||||||
export const execAsync = promisify( exec );
|
export const execAsync = promisify( exec );
|
||||||
|
|
||||||
|
// Map just the raw value types of IncomingMessage to a new type for the response which includes a body string.
|
||||||
|
type HttpsResponse = {
|
||||||
|
// I think it's fine to use this type this way just to exclude functions from the IncomingMessage type.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
[ K in keyof IncomingMessage as IncomingMessage[ K ] extends Function
|
||||||
|
? never
|
||||||
|
: K ]: IncomingMessage[ K ];
|
||||||
|
} & {
|
||||||
|
body: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A wrapper around https.request that returns a promise encapulating the response body and other response attributes.
|
||||||
|
export const requestAsync = (
|
||||||
|
options: string | RequestOptions | URL,
|
||||||
|
data?: string | Uint8Array | Buffer
|
||||||
|
) => {
|
||||||
|
return new Promise< HttpsResponse >( ( resolve, reject ) => {
|
||||||
|
const req = request( options, ( res ) => {
|
||||||
|
let body = '';
|
||||||
|
res.setEncoding( 'utf8' );
|
||||||
|
res.on( 'data', ( chunk ) => {
|
||||||
|
body += chunk;
|
||||||
|
} );
|
||||||
|
res.on( 'end', () => {
|
||||||
|
const httpsResponse: HttpsResponse = {
|
||||||
|
...res,
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
resolve( httpsResponse );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
req.on( 'error', ( err ) => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
if ( data ) {
|
||||||
|
req.write( data );
|
||||||
|
}
|
||||||
|
req.end();
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
|
@ -9,12 +9,39 @@ import chalk from 'chalk';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import CodeFreeze from './code-freeze/commands';
|
import CodeFreeze from './code-freeze/commands';
|
||||||
|
import Slack from './slack/commands/slack';
|
||||||
|
import { Logger } from './core/logger';
|
||||||
|
import { isGithubCI } from './core/environment';
|
||||||
|
|
||||||
console.log(
|
if ( ! isGithubCI() ) {
|
||||||
chalk.rgb( 150, 88, 138 ).bold( figlet.textSync( 'WooCommerce Utilities' ) )
|
Logger.notice(
|
||||||
);
|
chalk
|
||||||
|
.rgb( 150, 88, 138 )
|
||||||
|
.bold( figlet.textSync( 'WooCommerce \n Utils' ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const program = new Command()
|
const program = new Command()
|
||||||
.name( 'utils' )
|
.name( 'utils' )
|
||||||
.description( 'Monorepo utilities' )
|
.description( 'Monorepo utilities' )
|
||||||
.addCommand( CodeFreeze );
|
.addCommand( CodeFreeze )
|
||||||
|
.addCommand( Slack );
|
||||||
|
|
||||||
|
program.exitOverride();
|
||||||
|
|
||||||
|
const run = async () => {
|
||||||
|
try {
|
||||||
|
// parseAsync handles cases where the action is async and not async.
|
||||||
|
await program.parseAsync( process.argv );
|
||||||
|
} catch ( e ) {
|
||||||
|
// if github ci, always error
|
||||||
|
if ( isGithubCI() ) {
|
||||||
|
Logger.error( e );
|
||||||
|
} else if ( e.code !== 'commander.help' ) {
|
||||||
|
// if not github ci, only error if not help
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Slack Utilities
|
||||||
|
|
||||||
|
Utilities for automated posting to Slack.
|
||||||
|
|
||||||
|
To see available commands run `pnpm utils slack --help` from the project root.
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { slackMessageCommand } from './slack-message';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
const program = new Command( 'slack' )
|
||||||
|
.description( 'Slack message sending utilities' )
|
||||||
|
.addCommand( slackMessageCommand, { isDefault: true } );
|
||||||
|
|
||||||
|
export default program;
|
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../core/logger';
|
||||||
|
import { requestAsync } from '../../../core/util';
|
||||||
|
|
||||||
|
type SlackResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const slackMessageCommand = new Command( 'message' )
|
||||||
|
.description( 'Send a plain-text message to a slack channel' )
|
||||||
|
.argument(
|
||||||
|
'<token>',
|
||||||
|
'Slack authentication token bearing required scopes.'
|
||||||
|
)
|
||||||
|
.argument( '<text>', 'Text based message to send to the slack channel.' )
|
||||||
|
.argument(
|
||||||
|
'<channels...>',
|
||||||
|
'Slack channels to send the message to. Pass as many as you like.'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'--dont-fail',
|
||||||
|
'Do not fail the command if a message fails to send to any channel.'
|
||||||
|
)
|
||||||
|
.action( async ( token, text, channels, { dontFail } ) => {
|
||||||
|
Logger.startTask(
|
||||||
|
`Attempting to send message to Slack for channels: ${ channels.join(
|
||||||
|
','
|
||||||
|
) }`
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldFail = ! dontFail;
|
||||||
|
|
||||||
|
for ( const channel of channels ) {
|
||||||
|
// Define the request options
|
||||||
|
const options = {
|
||||||
|
hostname: 'slack.com',
|
||||||
|
path: '/api/chat.postMessage',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${ token }`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { statusCode, body } = await requestAsync(
|
||||||
|
options,
|
||||||
|
JSON.stringify( { channel, text } )
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger.endTask();
|
||||||
|
|
||||||
|
const response = JSON.parse( body ) as SlackResponse;
|
||||||
|
|
||||||
|
if ( ! response.ok || statusCode !== 200 ) {
|
||||||
|
Logger.error(
|
||||||
|
`Slack API returned an error: ${ response?.error }, message failed to send to ${ channel }.`,
|
||||||
|
shouldFail
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Logger.notice(
|
||||||
|
`Slack message sent successfully to channel: ${ channel }`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch ( e: unknown ) {
|
||||||
|
Logger.error( e, shouldFail );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
Loading…
Reference in New Issue