-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: implement basic hook logic #4
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Left a few comments
.github/workflows/flake8.yml
Outdated
@@ -11,7 +11,7 @@ jobs: | |||
timeout-minutes: 2 | |||
strategy: | |||
matrix: | |||
python-version: ["3.12"] | |||
python-version: ["3.11"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add a comment mentioning the reason behind this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally thought that flake8 did not support python 3.12 but flake8 6.1.0 has fixed this, I therefor update the format dependencies of the project
slack_cli_hooks/hooks/start.py
Outdated
SLACK_APP_TOKEN = "SLACK_APP_TOKEN" | ||
|
||
|
||
class EnvVarHandler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to better understand the benefit of having this class. This class does destructive changes to real os.environ. In this case, is the state represented by self._is_set
necessary? If the main benefit of this is to have the clean
method, these methods could be simple static functions instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, The goal of EnvVarHandler is to allow users to set their own environment variable values and not have them over-written and deleted by this hooks implementation
If a user sets the environment variable SLACK_APP_TOKEN then the app should use it and its value should still be present after the program terminates
self._is_set
is necessary since it dictates wether the environment variable was set by the user prior to the execution of the program
Let me know if this should be renamed or if there is a better implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry I misread the code and now I understand the intention of it.
Allowing devs to go with their own env variables should be fine but we can make the underlying behavior more visible, especially when the CLI associated tokens are unused. When a dev maintains a few Slack apps, they can unintentionally have env variables like SLACK_BOT_TOKEN in a terminal shell session. How about adding logging outputs like your SLACK_BOT_TOKEN env variable is used over the CLI's tokens etc.?
Also, I'd suggest renaming the class and methods to be even clearer:
# Let's add "os" to avoid confusion with `slack env`
os_env_vars = ManagedOSEnvVars()
try:
os_env_vars.set_if_absent(from=SLACK_CLI_XOXB, to=SLACK_BOT_TOKEN)
os_env_vars.set_if_absent(from=SLACK_CLI_XAPP, to=SLACK_APP_TOKEN)
finally:
os_env_vars.clear()
You can adjust the details but I hope this psuedo code illustrates my idea well.
tests/mock_socket_mode_server.py
Outdated
from flask import Flask | ||
from flask_sockets import Sockets | ||
|
||
socket_mode_envelopes = json.dumps( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't examined which patterns are actualy used but if many of the test data are unused, you can simplifty the code as much as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point 💯 I've cleaned up this file to the minimal requirements
tests/mock_web_api_server.py
Outdated
"error": "invalid_auth", | ||
} | ||
|
||
oauth_v2_access_response = json.dumps( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't examined which patterns are actualy used but if many of the test data are unused, you can simplifty the code as much as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point 💯 I've cleaned up this file to the minimal requirements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done! Lots of progress here.
A majority of my comments are from a "README-driven development" perspective; as an outsider to the details of this project, I used the README to frame how I think about the project and that helps shape the project as well.
The test server is badass!
One question: I see we have "scenario tests" and "slack cli hooks" tests. I think the latter are unit tests. Is that right? And the former leverage the test server so are more integration style tests?
Another question re: testing: can we get code coverage reporting based on unit test execution?
slack_cli_hooks/hooks/get_hooks.py
Outdated
"start": f"{EXEC} -m slack_cli_hooks.hooks.start", | ||
}, | ||
"config": { | ||
"watcher": {"filter-regex": "^manifest\\.(json)$", "paths": ["."]}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably want to watch for .py
changes too, right? Since Bolt will manage the websocket connection, it will operate as a single long-running process (as opposed to how the deno SDK operates, where every event starts a new deno process). Therefore, the Bolt process needs to be restarted every time app source code is changed to reflect the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch 💯 I tried implementing this but can't seem to get it to work, I've brought it up to the wider team
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brining it up with the wider team reviled that the Slack CLI will not restart the start hook
I've decided to leave the "filter-regex": "^manifest\\.(json)$"
since support for autoreload will be added in follow up PRs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally fair! Thanks for following up on it.
from .protocol import Protocol | ||
|
||
|
||
class DefaultProtocol(Protocol): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn, this is clean! 💯
logging.error(msg, *args, **kwargs) | ||
|
||
def respond(self, data: str): | ||
print(self.boundary + data + self.boundary) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👨🍳 💋
Co-authored-by: Kazuhiro Sera <[email protected]>
Co-authored-by: Fil Maj <[email protected]>
Co-authored-by: Fil Maj <[email protected]>
Co-authored-by: Fil Maj <[email protected]>
Co-authored-by: Fil Maj <[email protected]>
.github/workflows/flake8.yml
Outdated
@@ -11,7 +11,7 @@ jobs: | |||
timeout-minutes: 2 | |||
strategy: | |||
matrix: | |||
python-version: ["3.12"] | |||
python-version: ["3.11"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally thought that flake8 did not support python 3.12 but flake8 6.1.0 has fixed this, I therefor update the format dependencies of the project
slack_cli_hooks/hooks/start.py
Outdated
SLACK_APP_TOKEN = "SLACK_APP_TOKEN" | ||
|
||
|
||
class EnvVarHandler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, The goal of EnvVarHandler is to allow users to set their own environment variable values and not have them over-written and deleted by this hooks implementation
If a user sets the environment variable SLACK_APP_TOKEN then the app should use it and its value should still be present after the program terminates
self._is_set
is necessary since it dictates wether the environment variable was set by the user prior to the execution of the program
Let me know if this should be renamed or if there is a better implementation
tests/mock_socket_mode_server.py
Outdated
from flask import Flask | ||
from flask_sockets import Sockets | ||
|
||
socket_mode_envelopes = json.dumps( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point 💯 I've cleaned up this file to the minimal requirements
tests/mock_web_api_server.py
Outdated
"error": "invalid_auth", | ||
} | ||
|
||
oauth_v2_access_response = json.dumps( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point 💯 I've cleaned up this file to the minimal requirements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thanks for addressing my comments.
|
||
app = App() | ||
## Simple project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! Eventually I imagine this entire section gets replaced with a sample, i.e. slack create -t slack-samples/bolt-starter-project
or whatever.
README.md
Outdated
Define the Slack CLI configuration in a file named `slack.json`. | ||
|
||
```json | ||
{ | ||
"hooks": { | ||
"get-hooks": "python3 -m slack_cli_hooks.hooks.get_hooks" | ||
"get-hooks": "python3 -X dev -m slack_cli_hooks.hooks.get_hooks" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oooo things I learned today: the -X
option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-X dev: enable Python Development Mode, introducing additional runtime checks that are too expensive to be enabled by default.
https://docs.python.org/3.12/using/cmdline.html#cmdoption-X
How much does the dev mode cost in terms of runtime performance? Since what the get-hooks does is fairly simple, it should be ignorable. That being said, we may should avoid recommending "dev" mode for production-deployed apps regardless of the cost. If we can enable this only for slack run
(or it works only when running slack run
), we don't need to worry about it at all though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it's a good concern / callout @seratch , as the get-hooks
hook gets invoked on every single CLI command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call 💯 Adding it only to the start
hook seems worth while, we can also remove it with minimal effect (but it might be harder to add later on)
README.md
Outdated
Define the Slack CLI configuration in a file named `slack.json`. | ||
|
||
```json | ||
{ | ||
"hooks": { | ||
"get-hooks": "python3 -m slack_cli_hooks.hooks.get_hooks" | ||
"get-hooks": "python3 -X dev -m slack_cli_hooks.hooks.get_hooks" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-X dev: enable Python Development Mode, introducing additional runtime checks that are too expensive to be enabled by default.
https://docs.python.org/3.12/using/cmdline.html#cmdoption-X
How much does the dev mode cost in terms of runtime performance? Since what the get-hooks does is fairly simple, it should be ignorable. That being said, we may should avoid recommending "dev" mode for production-deployed apps regardless of the cost. If we can enable this only for slack run
(or it works only when running slack run
), we don't need to worry about it at all though.
README.md
Outdated
"settings": { | ||
"org_deploy_enabled": true, | ||
"socket_mode_enabled": true, | ||
"token_rotation_enabled": false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this property required? If not, we can remove this line to make the example as simple as possible. I believe the default value for this is false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, this line is not required 💯
slack_cli_hooks/hooks/start.py
Outdated
SLACK_APP_TOKEN = "SLACK_APP_TOKEN" | ||
|
||
|
||
class EnvVarHandler: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry I misread the code and now I understand the intention of it.
Allowing devs to go with their own env variables should be fine but we can make the underlying behavior more visible, especially when the CLI associated tokens are unused. When a dev maintains a few Slack apps, they can unintentionally have env variables like SLACK_BOT_TOKEN in a terminal shell session. How about adding logging outputs like your SLACK_BOT_TOKEN env variable is used over the CLI's tokens etc.?
Also, I'd suggest renaming the class and methods to be even clearer:
# Let's add "os" to avoid confusion with `slack env`
os_env_vars = ManagedOSEnvVars()
try:
os_env_vars.set_if_absent(from=SLACK_CLI_XOXB, to=SLACK_BOT_TOKEN)
os_env_vars.set_if_absent(from=SLACK_CLI_XAPP, to=SLACK_APP_TOKEN)
finally:
os_env_vars.clear()
You can adjust the details but I hope this psuedo code illustrates my idea well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left two minor comments but this looks great to me now!
|
||
def set_if_absent(self, os_env_var: str, value: str) -> None: | ||
if os_env_var in os.environ: | ||
self._protocol.warning( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is not a problem, it'd be worth considering to go with info level instead. That being said, if only warn or above works in this scenario, warn level is fine too.
Co-authored-by: Kazuhiro Sera <[email protected]>
Summary
This PR aims to introduce an implementation of the Slack CLI interface that enables the Slack CLI to interact with Bolt Python projects
Testing
Follow the instructions in the README.md
When it asks to
pip install slack-cli-hooks
execute the following insteadpip install -i https://test.pypi.org/simple/ slack-cli-hooks==0.0.0.dev1
Special notes
Requirements
./scripts/install_and_run_tests.sh
after making the changes.