From eb117482d73d4c93d8b93654559a969b201cc3d8 Mon Sep 17 00:00:00 2001 From: Tim Jacomb <21194782+timja@users.noreply.github.com> Date: Tue, 7 May 2024 15:11:33 +0100 Subject: [PATCH] Add better error handling for scopes and add app manifest (#963) --- README.md | 119 ++++++++---------- .../slack/pipeline/SlackUploadFileRunner.java | 13 +- 2 files changed, 64 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 276a13d1..5ca8ba17 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,8 @@ applications like [RocketChat][rocketchat] and [Mattermost][mattermost]. ## Install Instructions for Slack -1. Get a Slack account: -2. Configure the Jenkins integration: -3. Install this plugin on your Jenkins server: +1. Get a Slack account: +2. Install this plugin on your Jenkins server: 1. From the Jenkins homepage navigate to `Manage Jenkins` 2. Navigate to `Manage Plugins`, @@ -22,6 +21,57 @@ applications like [RocketChat][rocketchat] and [Mattermost][mattermost]. ![image][img-plugin-manager] +3. Continue to the next section to create your Slack app. + +### Creating your app + +1. Go to and click "Create New App". +2. Click `From an app manifest` +3. Select your workspace +4. Delete the example manifest that Slack provides +5. Click YAML +6. Paste the following into the text box: + +```yaml +display_information: + name: Jenkins +features: + bot_user: + display_name: Jenkins + always_online: true +oauth_config: + scopes: + bot: + - channels:read + - chat:write + - chat:write.customize + - files:write + - reactions:write + - users:read + - users:read.email + - groups:read +settings: + org_deploy_enabled: false + socket_mode_enabled: false + token_rotation_enabled: false +``` + +7. Click "Next" +8. Click "Create" +9. Click "Install App to Workspace" +10. Click "Allow" +11. Click "OAuth & Permissions" in the sidebar +9. Copy the "Bot User OAuth Access Token" +10. *On Jenkins*: Find the Slack configuration in "Manage Jenkins → System". + 1. *On Jenkins*: Click "Add" to create a new "Secret text" Credential with the bot user token. + 2. *On Jenkins*: Select the new "Secret text" in the dropdown. + 3. *On Jenkins*: Add a default channel (this can be removed after validating the connection works). + 4. *On Jenkins*: Tick the "Custom slack app bot user" option. +11. Invite the Jenkins bot user into the Slack channel(s) you wish to be notified in. +12. *On Jenkins*: Click test connection. A message will be sent to the default channel / default member. + +### Notify for all jobs + If you want to configure a notification to be sent to Slack for **all jobs**, you may want to also consider installing an additional plugin called [Global Slack Notifier plugin](https://plugins.jenkins.io/global-slack-notifier). ### Pipeline job @@ -228,17 +278,6 @@ Any hex triplet (i.e. `'#AA1100'`) can be used for the color of the message. The 1. Configure it in your Jenkins job (and optionally as global configuration) and **add it as a Post-build action**. -## Install Instructions for Slack compatible application - -1. Log into the Slack compatible application. -2. Create a Webhook (it may need to be enabled in system console) by visiting - Integrations. -3. You should now have a URL with a token. Something like - `https://mydomain.com/hooks/xxxx` where `xxxx` is the integration token and - `https://mydomain.com/hooks/` is the `Slack compatible app URL`. -4. Install this plugin on your Jenkins server. -5. Follow the freestyle or pipeline instructions for the slack installation instructions. - ## Security Use Jenkins Credentials and a credential ID to configure the Slack integration @@ -285,62 +324,12 @@ unclassified: slackNotifier: teamDomain: # i.e. your-company (just the workspace name not the full url) tokenCredentialId: slack-token + botUser: true ``` For more details see the configuration as code plugin documentation: -## Bot user mode - -There's two ways to authenticate with slack using this plugin. - -1. Using the "Jenkins CI" app written by Slack, - it's what is known as a 'legacy app' written directly into the slack code base and not maintained anymore. - -2. Creating your own custom "Slack app" and installing it to your workspace. - -The benefit of using your own custom "Slack app" is that you get to use all of the modern features that Slack has released in the last few years to -Slack apps and not to legacy apps. - -These include: - -- Threading -- File upload -- Custom app emoji per message -- Blocks - -The bot user option is not supported if you use the _Slack compatible app URL_ -option. - -### Creating your app - -Note: These docs may become outdated as Slack changes their website, if they do become outdated please send a PR here to update the docs. - -1. Go to and click "Create New App". -2. Pick an app name, i.e. "Jenkins" and a workspace that you'll be installing it to. -3. Click "Create App". This will leave you on the "Basic Information" screen for your new app. -3. Scroll down to "Display Information" and fill it out. You can get the Jenkins logo from: . -4. Scroll back up to "Add features and functionality". -5. Click "Permissions" to navigate to the "OAuth & Permissions" page. -6. Scroll down to "Scopes". Under "Bot Token Scopes" - 1. Add `chat:write` Scope. - 2. (optional) Add `files:write` Scope if you will be uploading files. - 3. (optional) Add `chat:write.customize` Scope if you will be sending messages with a custom username and/or avatar. - 4. (optional) Add `reactions:write` Scope if you will be [adding reactions](#emoji-reactions). - 5. (optional) Add `users:read` and `users:read.email` Scope if you will be [looking users up by email](#user-id-look-up). -7. (optional) Click "App Home" in the sidebar - 1. (optional) Edit the slack display name for the bot. - 2. Return to the "OAuth & Permissions" page. -8. At the top of the page, click "Install App to Workspace". This will generate a "Bot User OAuth Access Token". -9. Copy the "Bot User OAuth Access Token". -10. *On Jenkins*: Find the Slack configuration in "Manage Jenkins → Configure System". - 1. *On Jenkins*: Click "Add" to create a new "Secret text" Credential with that token. - 2. *On Jenkins*: Select the new "Secret text" in the dropdown. - 3. *On Jenkins*: Make note of the "Default channel / member id". - 4. *On Jenkins*: Tick the "Custom slack app bot user" option. -11. Invite the Jenkins bot user into the Slack channel(s) you wish to be notified in. -12. *On Jenkins*: Click test connection. A message will be sent to the default channel / default member. - ## Troubleshooting connection failure When testing the connection, you may see errors like: diff --git a/src/main/java/jenkins/plugins/slack/pipeline/SlackUploadFileRunner.java b/src/main/java/jenkins/plugins/slack/pipeline/SlackUploadFileRunner.java index cc5c833d..3fd363ca 100644 --- a/src/main/java/jenkins/plugins/slack/pipeline/SlackUploadFileRunner.java +++ b/src/main/java/jenkins/plugins/slack/pipeline/SlackUploadFileRunner.java @@ -162,14 +162,16 @@ private static JSONArray convertListToJsonArray(List fileIds) { return jsonArray; } - private static ResponseHandler getStandardResponseHandler() { + private ResponseHandler getStandardResponseHandler() { return response -> { int status = response.getStatusLine().getStatusCode(); if (status >= 200 && status < 300) { HttpEntity entity = response.getEntity(); return entity != null ? new JSONObject(EntityUtils.toString(entity)) : null; } else { - logger.log(Level.WARNING, UPLOAD_FAILED_TEMPLATE + status); + String errorMessage = UPLOAD_FAILED_TEMPLATE + status + " " + EntityUtils.toString(response.getEntity()); + listener.getLogger().println(errorMessage); + logger.log(Level.WARNING, errorMessage); return null; } }; @@ -191,7 +193,12 @@ private String convertChannelNameToId(String channelName, CloseableHttpClient cl ResponseHandler standardResponseHandler = getStandardResponseHandler(); JSONObject result = client.execute(requestBuilder.build(), standardResponseHandler); - if (result == null || !result.getBoolean("ok")) { + if (result == null) { + // logging should have been done in the result handler where the response is available + return null; + } + if (!result.getBoolean("ok")) { + listener.getLogger().println("Couldn't convert channel name to ID in Slack: " + result); return null; }