Skip to content

Commit

Permalink
Introduce a @slack/cli-hooks package that implements Slack CLI hooks (
Browse files Browse the repository at this point in the history
#1714)

Co-authored-by: Alissa Renz <[email protected]>
Co-authored-by: Fil Maj <[email protected]>
Co-authored-by: William Bergamin <[email protected]>
  • Loading branch information
4 people authored Jan 26, 2024
1 parent 46b500d commit f3dff4d
Show file tree
Hide file tree
Showing 19 changed files with 1,694 additions and 1 deletion.
11 changes: 10 additions & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
node-version: [18.x, 20.x]
package: [packages/logger, packages/oauth, packages/rtm-api, packages/socket-mode, packages/types, packages/web-api, packages/webhook]
package:
- packages/cli-hooks
- packages/logger
- packages/oauth
- packages/rtm-api
- packages/socket-mode
- packages/types
- packages/web-api
- packages/webhook
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
7 changes: 7 additions & 0 deletions packages/cli-hooks/.c8rc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"include": ["src/*.js"],
"exclude": ["**/*.spec.js"],
"reporter": ["lcov", "text"],
"all": false,
"cache": true
}
1 change: 1 addition & 0 deletions packages/cli-hooks/.eslintignore
188 changes: 188 additions & 0 deletions packages/cli-hooks/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// SlackAPI JavaScript style
// ---
// This style helps maintainers enforce safe and consistent programming practices in this project. It is not meant to be
// comprehensive on its own or vastly different from existing styles. The goal is to inherit and aggregate as many of
// the communities' recommended styles for the technologies used as we can. When, and only when, we have a stated need
// to differentiate, we add more rules (or modify options). Therefore, the fewer rules directly defined in this file,
// the better.
//
// These styles are a subset of the shared JavaScript and TypeScript configurations to only target JavaScript packages.

module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2022
},

// Ignore all build outputs and artifacts (node_modules, dotfiles, and dot directories are implicitly ignored)
ignorePatterns: [
'/coverage',
],

// These environments contain lists of global variables which are allowed to be accessed
//
// The target node version (v18) supports all needed ES2022 features: https://node.green
env: {
es2022: true,
node: true,
},

extends: [
// ESLint's recommended built-in rules: https://eslint.org/docs/rules/
'eslint:recommended',

// Node plugin's recommended rules: https://github.com/mysticatea/eslint-plugin-node
'plugin:node/recommended',

// AirBnB style guide (without React) rules: https://github.com/airbnb/javascript.
'airbnb-base',

// JSDoc plugin's recommended rules
'plugin:jsdoc/recommended',
],

rules: {
// JavaScript rules
// ---
// The top level of this configuration contains rules which apply to JavaScript.
//
// This section does not contain rules meant to override options or disable rules in the base configurations
// (ESLint, Node, AirBnb). Those rules are added in the final override.

// Eliminate tabs to standardize on spaces for indentation. If you want to use tabs for something other than
// indentation, you may need to turn this rule off using an inline config comments.
'no-tabs': 'error',

// Bans use of comma as an operator because it can obscure side effects and is often an accident.
'no-sequences': 'error',

// Disallow the use of process.exit()
'node/no-process-exit': 'error',

// Allow safe references to functions before the declaration.
'no-use-before-define': ['error', 'nofunc'],

// Allow scripts for hooks implementations.
'node/shebang': 'off',

// Allow unlimited classes in a file.
'max-classes-per-file': 'off',

// Disallow invocations of require(). This will help make imports more consistent and ensures a smoother
// transition to the best future syntax.
'import/no-commonjs': ['error', {
allowConditionalRequire: false,
}],

// Don't verify that all named imports are part of the set of named exports for the referenced module. The
// TypeScript compiler will already perform this check, so it is redundant.
'import/named': 'off',
'node/no-missing-import': 'off',

// Require extensions for imported modules
'import/extensions': ['error', 'ignorePackages', {
'js': 'always',
}],

// Allow use of import and export syntax, despite it not being supported in the node versions. Since this
// project is transpiled, the ignore option is used. Overrides node/recommended.
'node/no-unsupported-features/es-syntax': ['error', { ignores: ['modules'] }],

'operator-linebreak': ['error', 'after', {
overrides: { '=': 'none' }
}],
},

overrides: [
{
files: ['**/*.js'],
rules: {
// Override rules
// ---
// This level of this configuration contains rules which override options or disable rules in the base
// configurations in JavaScript.

// Increase the max line length to 120. The rest of this setting is copied from the AirBnB config.
'max-len': ['error', 120, 2, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],

// Restrict the use of backticks to declare a normal string. Template literals should only be used when the
// template string contains placeholders. The rest of this setting is copied from the AirBnb config.
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }],

// The server side Slack API uses snake_case for parameters often.
//
// For mocking and override support, we need to allow snake_case.
// Allow leading underscores for parameter names, which is used to acknowledge unused variables in TypeScript.
// Also, enforce camelCase naming for variables. Ideally, the leading underscore could be restricted to only
// unused parameter names, but this rule isn't capable of knowing when a variable is unused. The camelcase and
// no-underscore-dangle rules are replaced with the naming-convention rule because this single rule can serve
// both purposes, and it works fine on non-TypeScript code.
camelcase: 'error',
'no-underscore-dangle': 'error',
'no-unused-vars': [
'error',
{
varsIgnorePattern: '^_',
argsIgnorePattern: '^_'
}
],

// Allow cyclical imports. Turning this rule on is mainly a way to manage the performance concern for linting
// time. Our projects are not large enough to warrant this. Overrides AirBnB styles.
'import/no-cycle': 'off',

// Prevent importing submodules of other modules. Using the internal structure of a module exposes
// implementation details that can potentially change in breaking ways. Overrides AirBnB styles.
'import/no-internal-modules': ['error', {
// Use the following option to set a list of allowable globs in this project.
allow: [
'**/middleware/*', // the src/middleware directory doesn't export a module, it's just a namespace.
'**/receivers/*', // the src/receivers directory doesn't export a module, it's just a namespace.
'**/types/**/*',
'**/types/*', // type heirarchies should be used however one wants
],
}],

// Remove the minProperties option for enforcing line breaks between braces. The AirBnB config sets this to 4,
// which is arbitrary and not backed by anything specific in the style guide. If we just remove it, we can
// rely on the max-len rule to determine if the line is too long and then enforce line breaks. Overrides AirBnB
// styles.
'object-curly-newline': ['error', { multiline: true, consistent: true }],
},
},
{
files: ['src/**/*.spec.js'],
rules: {
// Test-specific rules
// ---
// Rules that only apply to JavaScript _test_ source files

// With Mocha as a test framework, it is sometimes helpful to assign
// shared state to Mocha's Context object, for example in setup and
// teardown test methods. Assigning stub/mock objects to the Context
// object via `this` is a common pattern in Mocha. As such, using
// `function` over the the arrow notation binds `this` appropriately and
// should be used in tests. So: we turn off the prefer-arrow-callback
// rule.
// See https://github.com/slackapi/bolt-js/pull/1012#pullrequestreview-711232738
// for a case of arrow-vs-function syntax coming up for the team
'prefer-arrow-callback': 'off',
// Using ununamed functions (e.g., null logger) in tests is fine
'func-names': 'off',
// In tests, don't force constructing a Symbol with a descriptor, as
// it's probably just for tests
'symbol-description': 'off',

'node/no-unpublished-import': ['error', {
"allowModules": ["mocha"],
}],
},
},
],
};
6 changes: 6 additions & 0 deletions packages/cli-hooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Node and NPM stuff
/node_modules
package-lock.json

# Coverage carryover
/coverage
22 changes: 22 additions & 0 deletions packages/cli-hooks/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIT License

Copyright (c) 2024- Slack Technologies, LLC

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
122 changes: 122 additions & 0 deletions packages/cli-hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Slack CLI Hooks

The `@slack/cli-hooks` package contains scripts that implement the contract
between the [Slack CLI][cli] and [Bolt for JavaScript][bolt].

## Overview

This library enables inter-process communication between the Slack CLI and
applications built with Bolt for JavaScript.

When used together, the CLI delegates various tasks to the Bolt application by
invoking processes ("hooks") and then making use of the responses provided by
each hook's `stdout`.

For a complete list of available hooks, read the [Supported Hooks][supported]
section.

## Requirements

This package supports Node v18 and higher. It's highly recommended to use [the
latest LTS version of Node][node].

An updated version of the Slack CLI is also encouraged while using this package.

## Installation

Add this package as a development dependency for your project with the following
command:

```sh
$ npm install --save-dev @slack/cli-hooks
```

Follow [the installation guide][install] to download the Slack CLI and easily
run the scripts included in this package.

## Usage

A Slack CLI-compatible Slack application includes a `slack.json` file that
contains hooks specific to that project. Each hook is associated with commands
that are available in the Slack CLI. By default, `get-hooks` retrieves all of
the [supported hooks][supported] and their corresponding scripts as defined in
this package.

The CLI will try to use the version of the `@slack/cli-hooks` specified in your
application's `package.json`. The hooks in this package are automatically added
to the `./node_modules/.bin` directory of your application when this package is
installed.

### Supported Hooks

The hooks that are currently supported for use within the Slack CLI include
`check-update`, `get-hooks`, `get-manifest`, and `start`:

| Hook Name | CLI Command | File |Description |
| -------------- | ---------------- | ---- | ----------- |
| `check-update` | `slack update` | [`check-update.js`](./src/check-update.js) | Checks the project's Slack dependencies to determine whether or not any packages need to be updated. |
| `get-hooks` | All | [`get-hooks.js`](./src/get-hooks.js) | Fetches the list of available hooks for the CLI from this repository. |
| `get-manifest` | `slack manifest` | [`get-manifest.js`](./src/get-manifest.js) | Converts a `manifest.json` file into a valid manifest JSON payload. |
| `start` | `slack run` | [`start.js`](./src/start.js) | While developing locally, the CLI manages a socket connection with Slack's backend and utilizes this hook for events received via this connection. |

### Overriding Hooks

To customize the behavior of a hook, add the hook to your application's
`slack.json` file and provide a corresponding script to be executed.

When commands are run, the Slack CLI will look to the project's hook definitions
and use those instead of what's defined in this library, if provided. Only
[supported hooks][supported] will be recognized and executed by the Slack CLI.

Below is an example `slack.json` file that overrides the default `start` hook:

```json
{
"hooks": {
"get-hooks": "npx -q --no-install -p @slack/cli-hooks slack-cli-get-hooks",
"start": "npm run dev"
}
}
```

### Troubleshooting

Sometimes the hook scripts are installed globally and might not be automatically
updated. To determine the source of these scripts, check the `node_modules/.bin`
directory of your project then run the following command:

```sh
$ which npx slack-cli-get-hooks # macOS / Linux
```

```cmd
C:\> where.exe npx slack-cli-get-hooks # Windows
```

These hooks can be safely removed and reinstalled at your application directory
to ensure you're using the correct version for your project.

## Getting help

If you get stuck, we're here to help. The following are the best ways to get
assistance working through your issue:

* [Issue Tracker][issues] for questions, feature requests, bug reports and
general discussion related to these packages. Try searching before you create
a new issue.
* [Email us][email]: `[email protected]`
* [Community Slack][community]: a Slack community for developers building all
kinds of Slack apps. You can find the maintainers and users of these packages
in **#lang-javascript**.

<!-- a collection of links -->
[bolt]: https://github.com/slackapi/bolt-js
[cli]: https://api.slack.com/automation/cli
[community]: https://community.slack.com/
[config]: https://api.slack.com/apps
[email]: mailto:[email protected]
[install]: https://api.slack.com/automation/cli/install
[issues]: http://github.com/slackapi/node-slack-sdk/issues
[manifest]: https://api.slack.com/reference/manifests
[node]: https://github.com/nodejs/Release#release-schedule
[supported]: #supported-hooks
Loading

0 comments on commit f3dff4d

Please sign in to comment.