diff --git a/.github/resources/.actions/cli-runner/action.yml b/.github/resources/.actions/cli-runner/action.yml new file mode 100644 index 00000000..c60d6b7f --- /dev/null +++ b/.github/resources/.actions/cli-runner/action.yml @@ -0,0 +1,130 @@ +name: "Slack: Run a CLI command" +description: "Install the Slack CLI to run a command" + +inputs: + command: + description: "Slack CLI command to run. Should not include the 'slack' prefix." + type: string + default: "" + required: true + verbose: + description: "Verbose flag" + type: boolean + default: false + required: false + cli-version: + description: "CLI Version" + type: string + default: "3.6.1" + required: false + app_id: + description: "App ID" + type: string + default: "" + required: false + +outputs: + ok: + description: "If the command completed without an error." + value: ${{ steps.run-slack-cli-command.outputs.ok || steps.run-slack-cli-command-windows.outputs.ok }} + exit_code: + description: "Exit code" + value: ${{ steps.run-slack-cli-command.outputs.exit_code || steps.run-slack-cli-command-windows.outputs.exit_code }} + command_executed: + description: "Command ran" + value: ${{ steps.run-slack-cli-command.outputs.command_executed || steps.run-slack-cli-command-windows.outputs.command_executed }} + output: + description: "Command output" + value: ${{ steps.run-slack-cli-command.outputs.output || steps.run-slack-cli-command-windows.outputs.output }} + +runs: + using: composite + steps: + - name: Cache Slack CLI + id: cache-cli + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 + 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 }} + + - 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" + + - 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 }} + + - 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 + VERBOSE: ${{ inputs.verbose }} + run: | + cmd="slack --skip-update ${{ inputs.command }}" + if [ "${{ inputs.verbose }}" == "true" ]; then + cmd="$cmd --verbose" + fi + cmd="$cmd --skip-update" + + output=$(eval $cmd 2>&1) + exit_code=$? + + echo "Command output: $output" + echo "Exit code: $exit_code" + + echo "ok=$([ $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 "output<> $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 + 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" + + "ok=$($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 + + "output<> "$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" - - - 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 - - - name: Run Slack CLI Command - shell: bash - env: - SLACK_SERVICE_TOKEN: $SLACK_SERVICE_TOKEN - SLACK_BOT_TOKEN: $SLACK_BOT_TOKEN - VERBOSE: ${{ inputs.verbose }} - run: | - echo "Running command: ${{ inputs.command }}" - if [ "${{ inputs.verbose }}" == "true" ]; then - slack ${{ inputs.command }} --verbose --skip-update - else - slack ${{ inputs.command }} --skip-update - fi - \ No newline at end of file diff --git a/.github/workflows/cli-runner-test.yml b/.github/workflows/cli-runner-test.yml new file mode 100644 index 00000000..91f4fb6c --- /dev/null +++ b/.github/workflows/cli-runner-test.yml @@ -0,0 +1,161 @@ +name: Slack CLI Runner Tests + +on: + pull_request: + +jobs: + test-all: + runs-on: ubuntu-latest + steps: + - name: Checkout the current code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Run slack version + id: version + uses: ./.github/resources/.actions/cli-runner + with: + command: "version" + cli-version: "3.6.1" + env: + SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} + + - name: Debug outputs + run: | + echo "output: '${{ steps.version.outputs.output }}'" + echo "ok: '${{ steps.version.outputs.ok }}'" + echo "command executed: '${{ steps.version.outputs.command_executed }}'" + + - name: Verify CLI version + if: steps.version.outputs.ok != 'true' + run: | + echo "CLI version command failure" + echo "output: ${{ steps.version.outputs.output }}" + exit 1 + + - name: Empty command + id: empty-command + uses: ./.github/resources/.actions/cli-runner + with: + command: "" + cli-version: "3.6.1" + 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: "3.6.1" + 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: "3.6.1" + env: + SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} + + - name: Ensure empty command works + if: steps.empty-command.outputs.ok != 'true' + run: | + echo "Empty command failure" + echo "ok: '${{ steps.empty-command.outputs.ok }}'" + echo "output: '${{ steps.empty-command.outputs.output }}'" + exit 1 + + - name: Ensure command with whitespace + if: steps.command-with-whitespace.outputs.ok != 'true' + run: | + echo "Command with whitespace failure" + echo "ok: '${{ steps.command-with-whitespace.outputs.ok }}'" + echo "output: '${{ steps.command-with-whitespace.outputs.output }}'" + exit 1 + + - name: Ensure long command works + if: steps.long-command.outputs.ok != 'true' + run: | + echo "Long command failure" + echo "ok: '${{ steps.long-command.outputs.ok }}'" + echo "output: '${{ steps.long-command.outputs.output }}'" + exit 1 + + - name: Run with verbose + id: with-verbose + uses: ./.github/resources/.actions/cli-runner + with: + command: "help" + cli-version: "3.6.1" + verbose: "true" + env: + SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} + + - name: Ensure verbose flag worked + if: steps.with-verbose.outputs.ok != 'true' + run: | + echo "Verbose flag failure" + echo "ok: '${{ steps.with-verbose.outputs.ok }}'" + echo "output: '${{ steps.with-verbose.outputs.output }}'" + exit 1 + + - name: First run (install if missing) + id: no-cache + uses: ./.github/resources/.actions/cli-runner + with: + command: "version" + cli-version: "3.6.1" + 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: "3.6.1" + 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.ok != '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: "3.6.1" + env: + SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} + + - name: Expect failure + if: steps.invalid-command.outputs.ok == 'true' + run: | + echo "Expected failure with invalid command" + echo "output: '${{ steps.invalid-command.outputs.output }}'" + echo "ok: '${{ steps.invalid-command.outputs.ok }}'" + 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.output, '3.5.0') }} + run: | + echo "Specific version failure" + echo "output: '${{ steps.specific-version.outputs.output }}'" + exit 1 \ No newline at end of file diff --git a/.github/workflows/cli-runner.yml b/.github/workflows/cli-runner.yml new file mode 100644 index 00000000..305ac66e --- /dev/null +++ b/.github/workflows/cli-runner.yml @@ -0,0 +1,54 @@ +name: Slack CLI Command Runner + +on: + workflow_call: + inputs: + command: + description: 'Slack CLI command to run. Should not include the 'slack' prefix.' + type: string + default: "" + required: true + verbose: + description: 'Verbose flag' + type: boolean + default: false + required: false + cli-version: + description: 'CLI version' + type: string + default: "3.6.1" + required: false + app_id: + description: "App ID" + type: string + default: "" + required: false + +env: + SLACK_DISABLE_TELEMETRY: "true" + +jobs: + run-slack-cli: + permissions: + contents: read + runs-on: ubuntu-latest + outputs: + ok: ${{ steps.run-slack-cli-command.outputs.ok }} + command_executed: ${{ steps.run-slack-cli-command.outputs.command_executed }} + output: ${{ steps.run-slack-cli-command.outputs.output }} + timeout-minutes: 5 + + steps: + - name: Checkout the current code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Install Slack CLI and Run Command + id: run-slack-cli-command + uses: ./.github/resources/.actions/cli-runner + with: + command: ${{ github.event.inputs.command }} + verbose: ${{ github.event.inputs.verbose == 'true' || github.run_attempt > 1 }} + cli-version: ${{ github.event.inputs.cli-version }} + app_id: ${{ github.event.inputs.app_id }} + env: + SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/slack-cli-github-action.yml b/.github/workflows/slack-cli-github-action.yml deleted file mode 100644 index 1c852c45..00000000 --- a/.github/workflows/slack-cli-github-action.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Slack CLI Command Runner - -on: - workflow_dispatch: - inputs: - command: - description: 'Slack CLI command to run' - required: true - verbose: - description: 'Verbose flag' - type: boolean - default: false - cli_version: - description: 'Slack CLI version' - required: false - default: "latest" - -env: - SLACK_DISABLE_TELEMETRY: "true" - -jobs: - deploy: - permissions: - contents: read - runs-on: ubuntu-latest - timeout-minutes: 10 - - steps: - - uses: actions/checkout@v4 - - - name: Install Slack CLI and Run Command - uses: ./.github/.actions/slack-cli-runner - with: - command: ${{ github.event.inputs.command }} - verbose: ${{ github.run_attempt > 1 }} - cli_version: ${{ github.event.inputs.cli_version }} - env: - SLACK_SERVICE_TOKEN: ${{ secrets.SLACK_SERVICE_TOKEN }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} \ No newline at end of file diff --git a/docs/sending-techniques/sending-data-cli-runner/sending-data-cli-runner.md b/docs/sending-techniques/sending-data-cli-runner/sending-data-cli-runner.md new file mode 100644 index 00000000..3ab8ce7b --- /dev/null +++ b/docs/sending-techniques/sending-data-cli-runner/sending-data-cli-runner.md @@ -0,0 +1,102 @@ +--- +sidebar_label: Overview +--- + +# Run Slack CLI commands in GitHub Actions workflows + +This technique uses the Slack CLI in GitHub Actions to run commands through workflows. + +Setting up a CI/CD pipeline [requires](https://docs.slack.dev/tools/slack-cli/guides/authorizing-the-slack-cli/#ci-cd) authorization using a service token. [Service tokens](https://docs.slack.dev/tools/slack-cli/guides/authorizing-the-slack-cli/#obtain-token) are long-lived, non-rotatable user tokens that don’t expire. + +## Setup + +1. Add your service token as a [repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository). Settings > Security > Secrets and variables > Actions and click the "New repository secret" button. + + Name: SLACK_SERVICE_TOKEN + + Secret: the xoxp- service token + +2. Add this workflow to your repository. GitHub Actions workflow files must be stored in the .github/workflows directory + +```yaml +name: Slack CLI Command Runner +on: + workflow_dispatch: + inputs: + command: + description: 'Slack CLI command to run' + type: string + default: "" + required: true + verbose: + description: 'Verbose flag' + type: boolean + default: false + required: false + cli_version: + description: 'CLI version' + type: string + default: "latest" + required: false + app_id: + description: "App ID" + type: string + default: "" + required: false + +jobs: + run-slack-cli: + uses: slackapi/slack-github-action/.github/workflows/cli-runner.yml@main + with: + command: ${{ github.event.inputs.command }} + verbose: ${{ github.event.inputs.verbose == 'true' || github.run_attempt > 1 }} + cli_version: ${{ github.event.inputs.cli_version }} + app_id: ${{ github.event.inputs.app_id }} + secrets: inherit +``` + +3. Go to [Actions tab](https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manually-run-a-workflow#configuring-a-workflow-to-run-manually) in your GitHub repository. +4. Select the "Slack CLI Command Runner" workflow and click "Run workflow." +5. Enter your desired command without the 'slack' prefix (e.g., version). +6. Click "Run workflow" to execute the command. + +## Usage + +Instead of manual dispatch, you can configure your workflow to run automatically on specific GitHub events. Note that commands must be hardcoded +when using automatic triggers. The following examples show different trigger options: + +#### On Pull Request: +```yaml +name: Slack CLI Command Runner +on: + pull_request: + branches: [ main ] + +jobs: + run-slack-cli: + uses: slackapi/slack-github-action/.github/workflows/cli-runner.yml@main + with: + command: ${{ github.event.inputs.command }} + verbose: ${{ github.event.inputs.verbose == 'true' || github.run_attempt > 1 }} + cli_version: ${{ github.event.inputs.cli_version }} + app_id: ${{ github.event.inputs.app_id }} + secrets: inherit +``` + +#### On Push to Main: +```yaml +name: Slack CLI Command Runner +on: + push: + branches: [ main ] + +jobs: + run-slack-cli: + uses: slackapi/slack-github-action/.github/workflows/cli-runner.yml@main + with: + command: ${{ github.event.inputs.command }} + verbose: ${{ github.event.inputs.verbose == 'true' || github.run_attempt > 1 }} + cli_version: ${{ github.event.inputs.cli_version }} + app_id: ${{ github.event.inputs.app_id }} + secrets: inherit +```