[solved] `drone exec` parser failures

We have a .drone.yml that is valid YAML and runs just fine on Drone servers. However, when I try to run it locally via drone exec, it fails with parser errors:

  • 2019/04/28 13:09:26 yaml: line 275: did not find expected alphabetic or numeric character

And if I strip out all the comments with yq -y . then I get a different parser error:

  • 2019/04/28 13:10:27 yaml: line 247: mapping values are not allowed in this context

drone version 1.1.0

These message are coming from deep in scannerc.go, and, given the anchor/alias contexts for the errors, it looks like it’s gotten confused by && we have in some of our commands (our .drone.yml doesn’t use any anchors yet).

Is this fodder for a drone-cli bug report?

Please provide a YAML that can be used to replicate the error verbatim, as well as the version of the CLI you are using.

$ drone --version
drone version 1.1.0

When run with the attached YAML file I get the following:

$ drone exec
2019/04/28 22:38:04 yaml: line 275: did not find expected alphabetic or numeric character

If I strip out all comments via yq -y . and run with that YAML I get the following:

$ drone exec
2019/04/28 22:38:37 yaml: line 247: mapping values are not allowed in this context

I can’t find a way to attach the YAML file, only images, so here’s the whole file (sanitized):

# REDACTED Continuous Integration Continuous Deployment configuration script
# See:
# - ./devdocs/Drone-Configuration.md
# - https://docs.drone.io/user-guide/pipeline/

kind: pipeline
name: REDACTED

platform:
  os: linux
  arch: amd64

clone:
  depth: 15


volumes:

# For build steps that need docker engine themselves (e.g. to build an image)
- name: docker-sock
  host:
    path: /var/run/docker.sock

# For build steps that push/pull on AWS image repositories
- name: aws-creds
  host:
    path: REDACTED

# For build steps that access other hosts directly (e.g. to trigger deployments)
- name: ssh-creds
  host:
    path: REDACTED


steps:

  # Amortized cleanup; Dump info about the Docker engine upon which the Drone runs
  - name: preliminaries
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - docker images | grep -e 'REDACTED\(mysql\|backup\|renderer\|service\|client\)\|\(admin\|designer\|vendor\|client\)portal' | grep -e ' \(days\|weeks\) ' | awk '{print $3}' | sort -u | xargs docker rmi --force || true
      - docker info
      - docker ps

  # Garbage collect and removes older images in the Docker engine upon which the Drone runs
  - name: housekeeping
    # Branch shareddev is used so as to amortize the housekeeping work with the development cadence
    when:
      branch: [ master, shareddev, drone-pull ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - docker system prune --force

  # Pulls new base images from REDACTED into the Docker engine upon which the Drone runs
  # TODO:
  # - split this into a step for each image
  # - add removal of old base images, e.g.
       # Knowing this will at times remove images that then have to get
       # downloaded by the following pulls; slightly inefficient, but
       # it keeps things fresh
       # - docker images | grep -e 'REDACTED/REDACTED' | grep -e ' \(weeks\) ' | awk '{print $3}' | sort -u | xargs docker rmi --force || true
  # - start on regexps for branch names to simplify the branch selection
  - name: drone-pull
    when:
      branch: [ drone-pull ]
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - docker pull mariadb:10.3
      - docker pull node:10-slim
      - docker pull REDACTED/REDACTED

  # Quick checks on the syntax of global resources that subsequent steps use
  - name: global-setup-and-checks
    image: REDACTED/REDACTED
    commands:
    # Log some rather underspecified details...
    - /bin/true DRONE_BUILD_EVENT=${DRONE_BUILD_EVENT}
    - /bin/true DRONE_COMMIT_REF=${DRONE_COMMIT_REF}
    - /bin/true DRONE_COMMIT_BRANCH=${DRONE_COMMIT_BRANCH}
    - /bin/true DRONE_BRANCH=${DRONE_BRANCH}
    - /bin/true DRONE_SOURCE_BRANCH=${DRONE_SOURCE_BRANCH}
    - /bin/true DRONE_TARGET_BRANCH=${DRONE_TARGET_BRANCH}
    - /bin/true DRONE_REPO_BRANCH=${DRONE_REPO_BRANCH}
    # Put smoke-tests.sh in a well known place; check syntax
    - cp ./REDACTED/bin/smoke-tests.sh /drone/src/
    - bash -n /drone/src/smoke-tests.sh

  # Quick checks on the syntax of branch-specific resources that subsequent steps use
  - name: branch-specific-checks
    when:
      branch: [ shareddev, master, REDACTED,   cpdev, hughdev, karldev, zacdev ]
      event: [ push ]
    image: REDACTED/REDACTED
    commands:
    # Sanity check the naming confusion of Drone (confusing on
    # pull_request events, so doubt has crept in); we rely on
    # DRONE_SOURCE_BRANCH == DRONE_BRANCH == when-branch-name
    - test "${DRONE_COMMIT_BRANCH}" = "${DRONE_BRANCH}"
    - test "${DRONE_SOURCE_BRANCH}" = "${DRONE_BRANCH}"
    - test "${DRONE_TARGET_BRANCH}" = "${DRONE_BRANCH}"
    # Create a compose-docker-<deploymentTag>.yml in a well known place; check syntax
    - ./REDACTED/docker-compose.sh ${DRONE_SOURCE_BRANCH} > /drone/src/docker-compose-${DRONE_SOURCE_BRANCH}.yml
    - yq -y . /drone/src/docker-compose-${DRONE_SOURCE_BRANCH}.yml



  ##############################
  # BEGIN GROUP: build

  # Build and tag the images for the repo, both sha1 and branch tags
  # Boilerplate steps, anticipating vectorizing syntax for Drone... ;-)
  # TODO: don't do the tagging here, do it in the steps that push to the image repository

  # The REDACTEDmysql image is only for devs and their distinguished branches; that is, the shared sites do not get it.
  # It differs from a stock mariadb:10.3 image in that it has an SQL file in /docker-entrypoint-initdb.d
  - name: build-mysql
    when:
      branch:
        exclude: [ shareddev, master, REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=REDACTEDmysql
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}

  - name: build-renderer
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=REDACTEDrenderer
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}
      - ../tools/package-lock.sh /tmp/$service:package-lock.json:drone-cruft $service:${DRONE_COMMIT_SHA}

  - name: build-backup
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=REDACTEDbackup
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}

  - name: build-service
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=REDACTEDservice
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}
      - ../tools/package-lock.sh /tmp/$service:package-lock.json:drone-cruft $service:${DRONE_COMMIT_SHA}

  - name: build-client
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=REDACTEDclient
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}
      - ../tools/package-lock.sh /tmp/$service:package-lock.json:drone-cruft $service:${DRONE_COMMIT_SHA}

  - name: build-adminportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=adminportal
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}

  - name: build-vendorportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=vendorportal
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}

  - name: build-clientportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=clientportal
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}


  - name: build-designerportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: build
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - service=designerportal
      - cd $service
      - ./build $service:${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} ${DRONE_BUILD_LINK}

  # END GROUP: build
  ##############################


  # Tag with branch name and then Optimistically push branch-tagged images for developer branches
  - name: push-dev-images
    when:
      branch: [ cpdev, hughdev, karldev, zacdev ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    - name: aws-creds
      path: REDACTED
    commands:
      - eval $(REDACTED)
      - repo=REDACTED.REDACTED
      # We don't push the DRONE_COMMIT_SHA tag for dev work
      - for image in REDACTEDmysql REDACTEDbackup REDACTEDrenderer REDACTEDservice REDACTEDclient adminportal vendorportal designerportal clientportal; do echo && echo 'tag and push image:' $repo/$image:${DRONE_SOURCE_BRANCH} && docker tag $image:${DRONE_COMMIT_SHA} $repo/$image:${DRONE_SOURCE_BRANCH} && docker push $repo/$image:${DRONE_SOURCE_BRANCH} ; done

  # Optimistically deploy the stack for developer branches by copying stack config file to CICD host then telling CICD host to deploy that config
  - name: deploy-dev-stack
    when:
      branch: [ cpdev, hughdev, karldev, zacdev ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: ssh-creds
      path: REDACTED
    commands:
      # The REDACTED REDACTED: REDACTED
      - cicdUserHost=REDACTED@REDACTED
      - cicdPath=stacks/dev/${DRONE_SOURCE_BRANCH}
      # TODO: Find a way for devs to control if/how their mysql service (re)configures
      # Subtle: we want the mysql service in the dev stack to restart, so we change some (irrelevant) detail of its configuration; then, `docker stack deploy` will restart it due to the change
      - yq -y '.services.mysql.environment.REDACTED_ENV_NONCE = "${DRONE_COMMIT_SHA}"' < /drone/src/docker-compose-${DRONE_SOURCE_BRANCH}.yml > /drone/src/docker-compose.yml
      - scp /drone/src/docker-compose.yml $cicdUserHost:$cicdPath/docker-compose.yml
      - ssh $cicdUserHost stacks/bin/deploy $cicdPath
      - sleep 7
      - ssh $cicdUserHost stacks/bin/status $cicdPath
      #
      # Heads up: Strange, intermittent failure of this on the REDACTED side on Drone version 0.7.x, e.g.:
      # REDACTED
      #   + ssh $cicdUserHost stacks/bin/ip $cicdPath
      #   stacks/bin/ip: line 15: $/stack.sh: No such file or directory
      # Looks like $stackDir resolves to $ -- I've not seen that bash failure before -REDACTED
      # During debugging, 'echo stackDir: $stackDir' just echoed 'stackDir: $' -- see  REDACTED and note that stdout/stderr lines may be mis-interleaved
      # Could be a Drone/Docker-side problem, e.g. some race that causes '$cicdPath' to be sent to the container's bash as '$', which would cause the observed problem
      # For now, I'm commenting this out because it's purely informational
      #
      #- ssh $cicdUserHost stacks/bin/ip $cicdPath


  ##############################
  # BEGIN GROUP: service1

  # Start services in Drone (mostly for smoke-tests)

  # The service1 group services either have no dependencies, or are
  # robust against dependencies that aren't yet running

  # Note that the step names for services are DNS names for
  # containers, so they cannot be changed without fixing (some)
  # configs within the containers, smoke-tests, etc

  - name: mysql
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service1
    # TODO: Figure out if and how to test migrations that start from a
    # particular set of existing migrations...
    # image: REDACTEDmysql:${DRONE_COMMIT_SHA}
    image: mariadb:10.3
    command:
      - mysqld
      - --max_allowed_packet=134217728
      - --net_read_timeout=180
      - --net_write_timeout=180
    detach: true
    environment:
      # REDACTEDservice config for NODE_ENV=drone-test
      MYSQL_ROOT_PASSWORD: REDACTED
      MYSQL_DATABASE: REDACTED
      MYSQL_USER: REDACTED
      MYSQL_PASSWORD: REDACTED

  - name: REDACTEDservice
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service1
    # Note: this image waits for the DB port, with a timeout
    image: REDACTEDservice:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      MYSQL_ROOT_PASSWORD: REDACTED
      # See REDACTED
      NODE_ENV: drone-test
      BLOB_STORAGE_HOST: mysql
      BLOB_STORAGE_USER: REDACTED
      BLOB_STORAGE_PASSWORD: REDACTED
      BLOB_STORAGE_DATABASE: REDACTED

  - name: REDACTEDrenderer
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service1
    image: REDACTEDrenderer:${DRONE_COMMIT_SHA}
    detach: true

  - name: REDACTEDbackup
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service1
    image: REDACTEDbackup:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      REDACTED_BACKUP_PORT: REDACTED
      REDACTED_BACKUP_SITENAME: REDACTED
      REDACTED_BACKUP_BUCKET: REDACTED
      DB_HOST: mysql
      DB_PORT: '3306'
      DB_USERNAME: REDACTED
      DB_PASSWORD: REDACTED
      DB_DATABASE: REDACTED
      BLOB_STORAGE_DATABASE: REDACTED

  # Make sure service1 services are running
  - name: await-service1
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service1
    image: REDACTED/REDACTED
    # TODO: don't wait for the DB because it's not needed for smoke-tests: requires separation of migration into its own image
    commands:
      - await-port REDACTEDrenderer 123456
      - await-port REDACTEDbackup 123456
      - await-port mysql 123456
      - await-port REDACTEDservice 123456

  # END GROUP: service1
  ##############################


  ##############################
  # BEGIN GROUP: service2

  # Start services in Drone (mostly for smoke-tests)

  # The service2 group services assume availability of service1 services

  - name: REDACTEDclient
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    image: REDACTEDclient:${DRONE_COMMIT_SHA}
    detach: true

  - name: adminportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    # Note: this image waits for the DB port, with a timeout
    image: adminportal:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      DB_HOST: mysql
      DB_PORT: '3306'
      DB_DATABASE: REDACTED
      DB_USERNAME: REDACTED
      DB_PASSWORD: REDACTED

  - name: designerportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    # Note: this image waits for the DB port, with a timeout
    image: designerportal:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      DB_HOST: mysql
      DB_PORT: '3306'
      DB_DATABASE: REDACTED
      DB_USERNAME: REDACTED
      DB_PASSWORD: REDACTED

  - name: vendorportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    # Note: this image waits for the DB port, with a timeout
    image: vendorportal:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      DB_HOST: mysql
      DB_PORT: '3306'
      DB_DATABASE: REDACTED
      DB_USERNAME: REDACTED
      DB_PASSWORD: REDACTED

  - name: clientportal
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    # Note: this image waits for the DB port, with a timeout
    image: clientportal:${DRONE_COMMIT_SHA}
    detach: true
    environment:
      DB_HOST: mysql
      DB_PORT: '3306'
      DB_DATABASE: REDACTED
      DB_USERNAME: REDACTED
      DB_PASSWORD: REDACTED

  # Make sure service2 services are running
  - name: await-service2
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: service2
    image: REDACTED/REDACTED
    # TODO: don't wait for the DB because it's not needed for smoke-tests
    # Really that means to have migration logic be in its own image
    commands:
      - await-port REDACTEDclient 123456
      - await-port adminportal 123456
      - await-port designerportal 123456
      - await-port vendorportal 123456
      - await-port clientportal 123456

  # END GROUP: service2
  ##############################


  ##############################
  # Tests

  # Smoke-test the services running here in the Drone, i.e. that we started in the two groups above
  - name: drone-smoke-tests
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - /drone/src/smoke-tests.sh ${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} 1  REDACTEDrenderer REDACTEDservice REDACTEDclient   vendorportal designerportal REDACTEDbackup adminportal clientportal

  # Test REDACTEDrenderer in a separate container from the one started above that's used for smoke-tests
  - name: test-REDACTEDrenderer
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: tests
    image: REDACTEDrenderer:${DRONE_COMMIT_SHA}
    commands:
      - cd /REDACTEDrenderer/dist && npm run test

  # Test REDACTEDservice in a separate container from the one started above that's used for smoke-tests
  # Note: these tests use mysql DB service that was started above
  - name: test-REDACTEDservice
    when:
      branch:
        exclude: [ REDACTED, drone-pull ]
    group: tests
    image: REDACTEDservice:${DRONE_COMMIT_SHA}
    environment:
      NODE_ENV: drone-test
    commands:
      # Todo: Truncate all tables because REDACTED and all it implies
      # won't be consistent across commits; this will be faster than
      # reinitializing the DB every time.  E.g.  `npm run truncate-tables`
      # See Grrr in build-mariadb
      #
      # However: not truncating is also useful because can reveal test fragility,
      # e.g. assumptions about starting from an empty table!  One possibility
      # would be to truncate and then run the tests twice!
      #
      # Another possibility is to use a dev snapshot of the DB, as per
      # Docker Stack REDACTED-MySql-Dev-mgr (with database name handled
      # appropriately).  Then:
      # - run migrations
      # - run tests
      # - undo all migrations
      # - run migrations
      # - run tests
      #
      # A further consideration is the idea that migrations could be
      # made branch aware, so starting with DB from another branch
      # would be OK.  That feature would itself then have to be tested!
      #
      - cd /REDACTEDservice/dist && npm run drone-test


  ##############################
  # Deployments

  # Push built, vetted images for shared branches.  Tag images with both branch name and the commit sha1.
  # Branch name is used for deployment, sha1 tag supports rollback to known working commit.
  # Arguably, we should not do this for the shareddev branch.
  - name: push-shared-images
    when:
      branch: [ shareddev, master ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: aws-creds
      path: REDACTED
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - eval $(REDACTED)
      - repo=REDACTED.REDACTED
      # Push the DRONE_SOURCE_BRANCH for deployments; and we also push the DRONE_COMMIT_SHA tag to support rollbacks
      # We don't push REDACTEDmysql because that's only for convenience of devs
      # TODO: Vet that we have a way to test and deploy from DRONE_COMMIT_SHA-tagged images (rollback)
      - for image in REDACTEDbackup REDACTEDrenderer REDACTEDservice REDACTEDclient adminportal vendorportal clientportal designerportal ; do for tag in ${DRONE_SOURCE_BRANCH} ${DRONE_COMMIT_SHA} ; do echo && echo 'tag and push image:' $repo/$image:$tag && docker tag $image:${DRONE_COMMIT_SHA} $repo/$image:$tag && docker push $repo/$image:$tag ; done ; done


  # Ensure that the correct images are tagged for REDACTED and pushed to the image repository.
  # Subtle: REDACTED deployment doesn't build anything; it uses sha1-tagged images in the repo; this step pulls them, tags them for ${DRONE_SOURCE_BRANCH}, and pushes the ${DRONE_SOURCE_BRANCH}-tagged image to the repo
  - name: pull-tag-push-REDACTED-images
    when:
      branch: [ REDACTED ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: aws-creds
      path: REDACTED
    - name: docker-sock
      path: /var/run/docker.sock
    commands:
      - eval $(REDACTED)
      - repo=REDACTED.REDACTED
      # Pull each sha1 image from the repo, tag it ${DRONE_SOURCE_BRANCH}, and push it
      # Note: the image:<sha1tag> likely already exists in the Drone's Docker engine, but we don't want to rely on that, e.g. for the rollback scenario
      - for image in REDACTEDbackup REDACTEDrenderer REDACTEDservice REDACTEDclient adminportal vendorportal clientportal designerportal ; do echo && echo 'pull tag push image:' $image && docker pull $repo/$image:${DRONE_COMMIT_SHA} && echo tag && docker tag $repo/$image:${DRONE_COMMIT_SHA} $repo/$image:${DRONE_SOURCE_BRANCH} && echo push && docker push $repo/$image:${DRONE_SOURCE_BRANCH} ; done


  # Deploy stack for shared branches by copying stack config file to CICD host then telling CICD host to deploy that config
  - name: deploy-shared-stack
    when:
      branch: [ shareddev, master, REDACTED ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: ssh-creds
      path: REDACTED
    commands:
      # The REDACTED REDACTED: REDACTED
      - cicdUserHost=REDACTED@REDACTED
      # Path to where a tiny config shell script maps to a particular REDACTED and Docker Stack name on that REDACTED
      - cicdPath=stacks/dev/${DRONE_SOURCE_BRANCH}
      - scp /drone/src/docker-compose-${DRONE_SOURCE_BRANCH}.yml $cicdUserHost:$cicdPath/docker-compose.yml
      - ssh $cicdUserHost stacks/bin/deploy $cicdPath
      - sleep 7
      - ssh $cicdUserHost stacks/bin/status $cicdPath


  # Run smoke-tests on the deployed site
  - name: deployed-dev-smoke-tests
    when:
      branch: [ shareddev, master, REDACTED,  cpdev, hughdev, karldev, zacdev ]
      event: [ push ]
    image: REDACTED/REDACTED
    volumes:
    - name: docker-sock
      path: /var/run/docker.sock
    - name: ssh-creds
      path: REDACTED
    commands:
      - ip=$(ssh REDACTED@REDACTED stacks/bin/ip stacks/dev/${DRONE_SOURCE_BRANCH})
      - /bin/echo && /bin/echo 'IP:' $ip && /bin/echo
      # Smoke-test the branch/stack-specific live-site services
      - /drone/src/smoke-tests.sh ${DRONE_COMMIT_SHA} ${DRONE_SOURCE_BRANCH} 2  $ip $ip $ip  $ip $ip $ip $ip $ip


  # This step is recursive in that it pushes a commit to the REDACTED
  # branch on REDACTED, which then kicks off another Drone run.
  # Specifically: A successful Drone push job on branch master that
  # gets to this step will then (force) push the commit to branch
  # REDACTED, which then triggers the Drone to run again, this time on
  # branch REDACTED, which, if successful, will deploy the images on the
  # production site (in the deploy-shared-stack step).
  - name: push-REDACTED-branch-commit
    when:
      branch: [ master ]
      event: [ push ]
      # roll_back event ?
      # TODO: more thorough condition checking?
    image: docker:git
    commands:
    - test "${DRONE_COMMIT_BRANCH}" = master
    - test "${DRONE_SOURCE_BRANCH}" = master
    - test "${DRONE_TARGET_BRANCH}" = master
    - test "${DRONE_BRANCH}" = master
    - git push origin REDACTED

  # TODO: consider whether to do anything automated if a deployment has failed

  # Notify the Slack channel about plain old branches
  - name: notify-slack
    when:
      status: [ success, failure ]
      branch:
        exclude: [ shareddev, master, REDACTED,   cpdev, hughdev, karldev, zacdev,   drone-pull ]
    image: plugins/slack
    settings:
      webhook: REDACTED
      channel: REDACTED
      username: REDACTED
      icon_emoji: ":drone:"
      template: >
        {{#success build.status}}
          On ({{build.branch}}) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *{{build.status}}:* build {{build.number}} succeeded.   Omg...  early Christmas
        {{else}}
          On ({{build.branch}}) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *{{build.status}}:* build {{build.number}} failed.  Failurrreee!  Nooooo
        {{/success}}

  # Notify the Slack channel about the development branches
  - name: notify-slack-dev
    when:
      status: [ success, failure ]
      branch: [ shareddev, cpdev, hughdev, karldev, zacdev ]
      event: [ push ]
    image: plugins/slack
    settings:
      webhook: REDACTED
      channel: REDACTED
      username: REDACTED
      icon_emoji: ":drone:"
      template: >
        {{#success build.status}}
          On (*{{build.branch}}*) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *Good!  {{build.status}} on {{build.branch}}*
        {{else}}
          On ({{build.branch}}) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *Sigh....  {{build.status}} on {{build.branch}}*
        {{/success}}

  # Notify the Slack channel about the master and production branches
  - name: notify-slack-production
    when:
      status: [ success, failure ]
      branch: [ REDACTED, master ]
      event: [ push ]
    image: plugins/slack
    settings:
      webhook: REDACTED
      channel: REDACTED
      username: REDACTED
      icon_emoji: ":drone:"
      template: >
        {{#success build.status}}
          On ({{build.branch}}) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *Yes!  {{build.status}} on {{build.branch}}*
        {{else}}
          On ({{build.branch}}) by {{build.author}}, <{{build.link}}|{{repo.owner}}/{{repo.name}}/{{build.number}}>
          <https://REDACTED/{{repo.owner}}/{{repo.name}}/commit/{{build.commit}}|{{truncate build.commit 8}}> - {{build.message}}
          *Damn!  {{build.status}} is not an option on {{build.branch}}*
        {{/success}}

Summary

This text will be hidden

The root cause of the error is this line:

image: REDACTEDservice:${DRONE_COMMIT_SHA}

The empty commit sha results in the following, which cannot be parsed:

image: REDACTEDservice:

If you want to use DRONE_ variables with drone exec to more closely emulate the server runtime environment, they need to be passed to the command like this:

DRONE_COMMIT_SHA=... drone exec

OK. Thanks for the quick analysis. I should have caught that myself.

-Hugh