Slack integration doesn't support Windows platform

Orb version: circleci/slack@3.4.2

Error message: failed to start cmd: exec: "/bin/bash": file does not exist


Is it possible to workaround this issue using artifacts and sending all Slack notifications from a docker image?

You can indeed use build artifacts to pass the outcome of a job from a Windows workflow to a Linux workflow and then trick the Slack orb into reporting the status of the Windows workflow from the Linux workflow by overriding the environment variables it reads from. This is a very ugly solution, but I personally prefer it to having no Slack notifications at all on Windows. Another option would be to ditch the Slack orb altogether and hit the Slack API directly. Any feedback folks have on improving this workaround is quite welcome.

version: 2.1
orbs:
  slack: circleci/slack@4.4.2
  windows: circleci/windows@2.4.0
jobs:
  build:
    executor: windows/default
    steps:
      - run:
          name: Make directory for job metadata.
          command: mkdir C:/tmp/job_metadata
          shell: bash.exe
      - run:
          name: Write current job URL.
          command: echo "$CIRCLE_BUILD_URL" > C:/tmp/job_metadata/build_url.txt
          shell: bash.exe
...
      - run:
          when: on_fail
          name: Write job exit code 1 for failure.
          command: echo 1 > C:/tmp/job_metadata/exit_code.txt
          shell: bash.exe
      - run:
          when: on_success
          name: Write job exit code 0 for success.
          command: echo 0 > C:/tmp/job_metadata/exit_code.txt
          shell: bash.exe
      - store_artifacts:
          path: /tmp/job_metadata
          destination: build-job-metadata
  notify:
    docker:
      - image: cimg/base:stable
    resource_class: small
    steps:
      - run:
          name: Download any job metadata artifacts for latest completed job on this branch.
          command: >
            curl
            --header "Content-Type: application/json"
            --header "Accept: application/json"
            --header "Circle-Token: $CIRCLE_TOKEN"
            "https://circleci.com/api/v1.1/project/<< pipeline.project.type >>/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/latest/artifacts?branch=$CIRCLE_BRANCH&filter=completed" |
            grep --only-matching 'https://[^"]*build-job-metadata[^"]*' |
            xargs -I url wget --header "Circle-Token: $CIRCLE_TOKEN" url
      - run:
          name: Overwrite CIRCLE_JOB environment variable with build.
          command: echo export CIRCLE_JOB=build >> $BASH_ENV
      - run:
          name: Overwrite CIRCLE_BUILD_URL environment variable from prior job metadata.
          command: echo export CIRCLE_BUILD_URL=$(<build_url.txt) >> $BASH_ENV
      - run:
          name: Exit with code from prior job metadata.
          command: exit $(<exit_code.txt)
      - slack/notify:
          event: fail
          template: basic_fail_1
      - slack/notify:
          event: pass
          template: basic_success_1
workflows:
  build:
    jobs:
      - build
  # Take advantage of the fact that free CircleCI accounts are limited to
  # running one workflow at a time. If you are on a paid account, these
  # workflows will instead run in parallel. Consider submitting a pull request
  # to the Slack orb to use the bash.exe shell on Windows, using
  # https://github.com/eddiewebb/circleci-queue, or using the CircleCI API to
  # wait for the build workflow to complete.
  notify:
    jobs:
      - notify:
          context:
            # a context that defines the CIRCLE_TOKEN environment variable
            # to be a CircleCI API token for your project with Admin scope
            - circleci-api-admin
            # a context that defines the SLACK_ACCESS_TOKEN and SLACK_DEFAULT_CHANNEL
            # environment variables as specified by
            # https://github.com/CircleCI-Public/slack-orb#readme
            - slack

It turns out the use of artifacts is unnecessary, and it’s risky to assume that the most recent completed job on the same branch is the correct job to report the status of. A better solution is to search for the most recent completed job with the same commit hash using the CircleCI API. This also enables propagation of cancellations to the notify job. Put the following code in .circleci/trickSlackOrb.sh:

#!/bin/bash

set -eufo pipefail

# Issues a v1.1 CircleCI API request to the given relative path within this
# project, and outputs the JSON response to stdout. Specify the HTTP request
# method as the second argument. CIRCLE_TOKEN is not a CircleCI built-in and
# must be provided by a context. It must be a CircleCI API token with access to
# this project and the required scope. The environment variable VCS_TYPE is not
# a CircleCI built-in and should be set from the CircleCI config.yml file to
# << pipeline.project.type >>. See
# https://circleci.com/docs/2.0/pipeline-variables/#pipeline-values.
makeApiRequest() {
  curl \
    --request "$2" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --header "Circle-Token: $CIRCLE_TOKEN" \
    "https://circleci.com/api/v1.1/project/$VCS_TYPE/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/$1"
}

# Output the value of the given JSON key to stdout. The second parameter is the
# JSON string to search.
getJsonValue() {
  local match="$(grep --only-matching "\"$1\"\s*:\s*\"[^\"]*\"" <<< "$2")"
  # Get the value between the third and fourth double quotes.
  cut --delimiter=\" --fields=4 <<< "$match"
}

# Trick the Slack orb into reporting the status of the most recently completed
# job preceding this one that has the same VCS commit hash. Fail this job if the
# preceding one failed. Cancel this job if the preceding one was cancelled.
# Overwrite the CIRCLE_JOB environment variable with the prior job's name and
# the CIRCLE_BUILD_URL environment variable with the prior job's URL. The other
# CIRCLE_* environment variables that Slack reads from should be the same
# between this notify job and the corresponding prior job. If no corresponding
# job can be found, fail this job without tricking Slack.
trickSlackOrb() {
  local buildNum
  for ((buildNum = $CIRCLE_BUILD_NUM - 1; buildNum > 0; buildNum--))
  do
    # This API request requires that CIRCLE_TOKEN have at least the read only
    # scope. See https://circleci.com/docs/api/v1/#single-job.
    local jobMetadata="$(makeApiRequest "$buildNum" "GET")"

    local job="$(getJsonValue "CIRCLE_JOB" "$jobMetadata")"
    if [[ "$job" != "notify" ]] &&
      grep --quiet "\"vcs_revision\":\"$CIRCLE_SHA1\"" <<< "$jobMetadata" &&
      grep --quiet '"lifecycle":"finished"' <<< "$jobMetadata" &&
      grep --quiet '"dont_build":null' <<< "$jobMetadata"
    then
      if grep --quiet '"outcome":"canceled"' <<< "$jobMetadata"
      then
        # Cancels the currently executing CircleCI job, and outputs a JSON document
        # containing metadata regarding this job to stdout. See
        # https://circleci.com/docs/api/v1/#cancel-a-build. This API request
        # requires that CIRCLE_TOKEN token have the admin scope.
        makeApiRequest "$CIRCLE_BUILD_NUM/cancel" "POST"
        # Give the cancellation up to a minute to take effect before failing.
        sleep 60
        break
      fi
      local buildUrl="$(getJsonValue "build_url" "$jobMetadata")"
      echo "export CIRCLE_JOB=$job" >> $BASH_ENV
      echo "export CIRCLE_BUILD_URL=$buildUrl" >> $BASH_ENV
      grep --quiet '"outcome":"success"' <<< "$jobMetadata" && return
      break
    fi
  done
  exit 1
}

trickSlackOrb

Include an extra job in your .circleci/config.yml that runs the script and issues the Slack notification:

version: 2.1
orbs:
  slack: circleci/slack@4.4.2
  windows: circleci/windows@2.4.0
jobs:
  build:
    executor: windows/default
    steps:
...
  notify:
    docker:
      - image: cimg/base:stable
    resource_class: small
    steps:
      - checkout
      - run:
          name: Trick the Slack orb into reporting the status of the most recently finished preceding build job.
          command: source .circleci/trickSlackOrb.sh
          environment:
            VCS_TYPE: << pipeline.project.type >>
      - slack/notify:
          event: fail
          template: basic_fail_1
      - slack/notify:
          event: pass
          template: basic_success_1
workflows:
  build:
    jobs:
      - build
  # Take advantage of the fact that free CircleCI accounts are limited to
  # running one workflow at a time. If you are on a paid account, these
  # workflows will instead run in parallel. Consider submitting a pull request
  # to the Slack orb to use the bash.exe shell on Windows, using
  # https://github.com/eddiewebb/circleci-queue, or using the CircleCI API to
  # wait for the build workflow to complete.
  notify:
    jobs:
      - notify:
          context:
            # a context that defines the CIRCLE_TOKEN environment variable
            # to be a CircleCI API token for your project with Admin scope
            - circleci-api-admin
            # a context that defines the SLACK_ACCESS_TOKEN and SLACK_DEFAULT_CHANNEL
            # environment variables as specified by
            # https://github.com/CircleCI-Public/slack-orb#readme
            - slack