Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions .github/resources/.actions/cli-runner/action.yml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⭐ praise: We've discussed this setup and I think the separated action definition is a nice pattern to use for the CLI technique! Thanks for encouraging this!

👁️‍🗨️ thought: The exact calling syntax escapes me but for releases it'd be so interesting to find this pattern in calling workflows:

- uses: slackapi/slack-github-action/cli@v2
  with:
    command: version

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Slack CLI Installation and Command Runner
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name: Slack CLI Installation and Command Runner
name: "Slack: Run a CLI command"

🌲 suggestion: To match the action.yml file for other techniques!

description: Download and cache the Slack CLI and run the input command
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: Download and cache the Slack CLI and run the input command
description: "Install the Slack CLI to run a command"

🌞 suggestion: Perhaps idea on wording - I'm hoping to leave magic behind the scenes with this change but am not sure of the verbiage.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this! It's less verbose and more user-centric imo 🙇‍♀️


inputs:
command:
description: "Slack CLI command to run"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: "Slack CLI command to run"
description: "Slack CLI command to run. Should not include the 'slack' prefix."

📚 suggestion: We can document some of these requirements here for the code curious! I'm never sure if these are surfaced elsewhere though...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📚 suggestion: Also I think the descriptions for inputs and outputs can use more worded sentences with end punctuation. Starting with this now makes later updates more clear I find.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this feedback - agreed! 🙌

type: string
default: ""
required: true
verbose:
description: "Verbose flag"
type: boolean
default: false
required: false
cli_version:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cli_version:
cli-version:

🐳 suggestion: It's unclear in official documentation what the recommended pattern is for input attributes, but I think "kebab" case is used most often, but it might be nice to change these in this action.yml file?

🔗 https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows#example-reusable-workflow

description: "CLI Version"
type: string
default: "latest"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default: "latest"
default: "3.x.x"

🚀 suggestion(non-blocking): I'm hoping we can soon land floating version in slackapi/slack-cli#199! For now the installation script should default to the latest without such specifier though.

required: false
app_id:
description: "App ID"
type: string
default: ""
required: false

Comment on lines +20 to +25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
app_id:
description: "App ID"
type: string
default: ""
required: false

🪓 question: For now is this alright to remove? I believe it might conflict with a separate --app flag?

outputs:
success:
description: "Whether the command ran successfully"
value: ${{ steps.run-slack-cli-command.outputs.success }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
success:
description: "Whether the command ran successfully"
value: ${{ steps.run-slack-cli-command.outputs.success }}
ok:
description: "Whether the command ran successfully"
value: ${{ steps.run-slack-cli-command.outputs.ok }}

📺 suggestion: To match a similar value of other techniques-

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
success:
description: "Whether the command ran successfully"
value: ${{ steps.run-slack-cli-command.outputs.success }}
success:
description: "If the command completed without an error."
value: ${{ steps.run-slack-cli-command.outputs.success }}

📝 suggestion: Some quick ideas on the description here too.

exit_code:
description: "Exit code"
value: ${{ steps.run-slack-cli-command.outputs.exit_code }}
command_executed:
description: "Command ran"
value: ${{ steps.run-slack-cli-command.outputs.command_executed }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
command_executed:
description: "Command ran"
value: ${{ steps.run-slack-cli-command.outputs.command_executed }}

🪓 suggestion: Hmm... I'm not sure if we want to return this in addition to what the logs might show?

🗣️ ramble: There's a chance modifications from the verbose option don't appear with this and using the input command I believe will be alright for most use cases.

Copy link
Author

@ewanek1 ewanek1 Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm yea I echo back the command input stored from inputs command. I do have command_executed which is purely for the test file to ensure some of the outputs are as expected. If it's removed some of the tests might not be as rigorous though 🤔

stdout:
description: "Command output"
value: ${{ steps.run-slack-cli-command.outputs.stdout }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stdout:
description: "Command output"
value: ${{ steps.run-slack-cli-command.outputs.stdout }}
output:
description: "Command output"
value: ${{ steps.run-slack-cli-command.outputs.output }}

📫 suggestion: We might want to capture both stdout and stderr with this?


runs:
using: composite
steps:
- name: Cache Slack CLI
id: cache-cli
uses: actions/cache@v4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uses: actions/cache@v4
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4

📍 suggestion: Let's use a pinned version here to avoid unexpected upstream changes!

with:
path: |
${{ runner.os == 'Windows' && format('{0}/AppData/Local/slack-cli', env.USERPROFILE) || '~/.slack/bin' }}
key: slack-cli-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cli_version }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💰 praise: Super nice to be landing this in a first release. While installation is somewhat fast I think restoring from cache is often faster!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 question: I notice the ~ is used when checking this path but the $HOME variable is used following. Would using the $HOME variable here be possible too?


- name: Add Slack CLI to PATH (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: echo "$HOME/.slack/bin" >> "$GITHUB_PATH"

- name: Add Slack CLI to PATH (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: Add-Content -Path $env:GITHUB_PATH -Value "$env:USERPROFILE\.slack\bin"
Comment on lines +51 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👾 question: Are these steps needed just if the cache was found? Unsure if the installation script has similar effects or if this could be skipped...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes seems like I would need to move the action to the root level so slack-github-action/cli/action.yml. Instead of having it under .github/resources/.actions


- name: Install Slack CLI (Linux/macOS)
if:
(runner.os == 'Linux' || runner.os == 'macOS') &&
(steps.cache-cli.outputs.cache-hit != 'true')
shell: bash
run: |
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash -s -- -v ${{ inputs.cli_version }} --skip-update

- name: Install Slack CLI (Windows)
if:
runner.os == 'Windows' &&
(steps.cache-cli.outputs.cache-hit != 'true')
shell: pwsh
run: |
irm https://downloads.slack-edge.com/slack-cli/install-windows-dev.ps1 | iex -- -v ${{ inputs.cli_version }}

- name: Run Slack CLI Command (Linux/macOS)
if: runner.os != 'Windows'
id: run-slack-cli-command
shell: bash
env:
SLACK_SERVICE_TOKEN: $SLACK_SERVICE_TOKEN
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪬 suggestion: We might prefer to use an explicit token input of this workflow instead of checking environment variables

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if I misunderstand, but wouldn't the user need to repeatedly put their token as an input? Or is it still saved in secrets so they don't need to regularly retrieve it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ewanek1 You're right that the token input would be more verbose... But I think this makes workflows more clear when different tokens are needed in separate steps! 👾

I'm curious if we can fallback to environment variables if a token isn't provided in inputs but the order of which token to use, if app and bot tokens are options, might be confusing for other techniques too.

Also, I also confirmed that a token provided by flag isn't saved to file by the CLI to avoid later unexpected happenings with a service token, so this might be similar to current behavior as well?

VERBOSE: ${{ inputs.verbose }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⌛ question: Is this used as a variable or can we also remove it?

Suggested change
VERBOSE: ${{ inputs.verbose }}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this I never used this and it can be removed 😄

run: |
cmd="slack ${{ inputs.command }}"
if [ "${{ inputs.verbose }}" == "true" ]; then
cmd="$cmd --verbose"
fi
cmd="$cmd --skip-update"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
VERBOSE: ${{ inputs.verbose }}
run: |
cmd="slack ${{ inputs.command }}"
if [ "${{ inputs.verbose }}" == "true" ]; then
cmd="$cmd --verbose"
fi
cmd="$cmd --skip-update"
SLACK_SKIP_UPDATE: "true"
VERBOSE: ${{ inputs.verbose }}
run: |
cmd="slack ${{ inputs.command }}"
if [ "${{ inputs.verbose }}" == "true" ]; then
cmd="$cmd --verbose"
fi

🌐 suggestion(non-blocking): Using an environment variable instead might make this logic more clear?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - thanks!


output=$(eval $cmd 2>&1)
exit_code=$?

echo "Command output: $output"
echo "Exit code: $exit_code"

Comment on lines +94 to +96
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
echo "Command output: $output"
echo "Exit code: $exit_code"

🪓 thought: If these are duplicating outputs it might be alright to remove?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check if the exit code is necessary! I believe Actions resurfaces the error anyways.

echo "success=$([ $exit_code -eq 0 ] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
echo "command_executed=slack ${{ inputs.command }} --skip-update" >> $GITHUB_OUTPUT

echo "stdout<<EOF" >> $GITHUB_OUTPUT
echo "$output" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Run Slack CLI Command (Windows)
if: runner.os == 'Windows'
id: run-slack-cli-command-windows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍃 question: Do the outputs from this step also map to the outputs of the workflow? I notice a different id is used here and was not sure how these are mapping at this time...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an oversight thank you!!

shell: pwsh
env:
SLACK_SERVICE_TOKEN: $SLACK_SERVICE_TOKEN
VERBOSE: ${{ inputs.verbose }}
run: |
$cmd = "slack ${{ inputs.command }}"
if ("${{ inputs.verbose }}" -eq "true") { $cmd += " --verbose" }
$cmd += " --skip-update"

$output = & $cmd 2>&1
$exit_code = $LASTEXITCODE

Write-Host "Command output: $output"
Write-Host "Exit code: $exit_code"

"success=$($exit_code -eq 0)" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"exit_code=$exit_code" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"command_executed=$cmd" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

"stdout<<EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
$output | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"EOF" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

66 changes: 0 additions & 66 deletions .github/resources/.actions/slack-cli-runner/action.yml

This file was deleted.

160 changes: 160 additions & 0 deletions .github/workflows/cli-runner-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
name: Slack CLI Command Runner Tests

on:
pull_request:

jobs:
test-all:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- uses: actions/checkout@v4
- name: Checkout the current code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

👁️‍🗨️ suggestion: Guards against unexpected changes upstream!


- name: Run slack version
id: version
uses: ./.github/resources/.actions/cli-runner
with:
command: "version"
Comment on lines +13 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 suggestion: It might be interesting to run this test across multiple runners - ubuntu, mac, and windows?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Debug outputs
run: |
echo "stdout: '${{ steps.version.outputs.stdout }}'"
echo "success: '${{ steps.version.outputs.success }}'"
echo "command executed: '${{ steps.version.outputs.command_executed }}'"

- name: Verify CLI version
if: steps.version.outputs.success != 'true'
run: |
echo "CLI version command failure"
echo "stdout: ${{ steps.version.outputs.stdout }}"
exit 1

- name: Empty command
id: empty-command
uses: ./.github/resources/.actions/cli-runner
with:
command: ""
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Command with whitespace
id: command-with-whitespace
uses: ./.github/resources/.actions/cli-runner
with:
command: " version"
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Long command with flags
id: long-command
uses: ./.github/resources/.actions/cli-runner
with:
command: 'doctor --help --experiment string'
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 suggestion(non-blocking): We should follow up with a test that uses both the --app and token value to make sure all things reach the Slack API as expected:

manifest --app $SLACK_APP_ID

🗣️ ramble: I'm not certain if this environment variable can be gathered from the env but that'd be a nice thing to find in test I hope too?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed similar to what I have for verbose.


- name: Ensure empty command works
if: steps.empty-command.outputs.success != 'true'
run: |
echo "Empty command failure"
echo "success: '${{ steps.empty-command.outputs.success }}'"
echo "stdout: '${{ steps.empty-command.outputs.stdout }}'"
exit 1

- name: Ensure command with whitespace
if: steps.command-with-whitespace.outputs.success != 'true'
run: |
echo "Command with whitespace failure"
echo "success: '${{ steps.command-with-whitespace.outputs.success }}'"
echo "stdout: '${{ steps.command-with-whitespace.outputs.stdout }}'"
exit 1

- name: Ensure long command works
if: steps.long-command.outputs.success != 'true'
run: |
echo "Long command failure"
echo "success: '${{ steps.long-command.outputs.success }}'"
echo "stdout: '${{ steps.long-command.outputs.stdout }}'"
exit 1

- name: Run with verbose
id: with-verbose
uses: ./.github/resources/.actions/cli-runner
with:
command: "help"
cli_version: "latest"
verbose: "true"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Ensure verbose flag worked
if: steps.with-verbose.outputs.success != 'true'
run: |
echo "Verbose flag failure"
echo "success: '${{ steps.with-verbose.outputs.success }}'"
echo "stdout: '${{ steps.with-verbose.outputs.stdout }}'"
exit 1

- name: First run (install if missing)
id: no-cache
uses: ./.github/resources/.actions/cli-runner
with:
command: "version"
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Second run (cache hit)
id: cache-hit
uses: ./.github/resources/.actions/cli-runner
with:
command: "version"
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

- name: Ensure cache worked
if: github.run_attempt > 1 && steps.cache-hit.outputs.success != 'true'
run: |
echo "cache failure"
exit 1

- name: Run with invalid command
id: invalid-command
continue-on-error: true
uses: ./.github/resources/.actions/cli-runner
with:
command: "invalid-command"
cli_version: "latest"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Expect failure
if: steps.invalid-command.outputs.success == 'true'
run: |
echo "Expected failure with invalid command"
echo "stdout: '${{ steps.invalid-command.outputs.stdout }}'"
echo "success: '${{ steps.invalid-command.outputs.success }}'"
exit 1

- name: Test specific version
id: specific-version
uses: ./.github/resources/.actions/cli-runner
with:
command: "version"
cli_version: "3.5.0"
env:
SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }}

- name: Ensure specific version
if: ${{ !contains(steps.specific-version.outputs.stdout, '3.5.0') }}
run: |
echo "Specific version failure"
echo "stdout: '${{ steps.specific-version.outputs.stdout }}'"
exit 1
Loading
Loading