Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shell Script Moderno

Shell Script Moderno

Vídeo dessa palestra:
https://www.youtube.com/watch?v=XBkBnKmu94U

O shell script antigamente era executado em dois ambientes: via linha de comando ou via crontab. Hoje há diversas ferramentas que executam scripts, como Git, Puppet, Jenkins, Travis, GitLab CI e Docker.

Cada ferramenta tem seu próprio ambiente de execução, variáveis e detalhes a se levar em conta. Neste panorama, como fazer scripts robustos, portáveis e funcionais? Quais são os idioms que melhor se encaixam nessa nova realidade?

Palestra apresentada em 2016-07-15 no FISL 2016 (Porto Alegre-RS) http://softwarelivre.org/fisl17

Avatar for Aurelio Jargas

Aurelio Jargas

July 15, 2016
Tweet

More Decks by Aurelio Jargas

Other Decks in Programming

Transcript

  1. Sysadmin dos anos 90 Servidor físico (empoeirado) CD do Linux

    Instala a distro Recompila o kernel Instala pacotes Edita arquivos .conf Inicia serviços Feito!
  2. Sysadmin dos anos 90 Servidor físico (empoeirado) CD do Linux

    Instala a distro Recompila o kernel Instala pacotes Edita arquivos .conf install.sh Inicia serviços Feito! }
  3. Sysadmin / DevOps moderno AWS / Azure / Google Cloud

    Puppet / Chef / Ansible GitHub / GitLab / Bitbucket Jenkins Travis CI / GitLab CI Docker
  4. Ambientes de execução de scripts Linha de comando $ meu-script.sh

    foo Cron 0 * * * * root /bin/meu-script.sh foo
  5. Git

  6. Git hooks $ cat app.bare.git/hooks/post-receive #!/bin/bash -exu echo "Executando post-receive

    em producao" unset GIT_DIR cd /opt/repo/foobar.git git checkout producao git pull $
  7. Travis CI language: bash before_install: - url_base="https://raw.githubusercontent.com/aureliojargas" - curl -sOL

    "${url_base}/clitest/master/clitest" - chmod +x clitest - mv clitest testador script: - cd testador - ./run
  8. Travis CI language: bash before_install: - url_base="https://raw.githubusercontent.com/aureliojargas" - curl -sOL

    "${url_base}/clitest/master/clitest" - chmod +x clitest - mv clitest testador script: - cd testador - ./run
  9. GitLab CI code_lint: script: - date - uname -a -

    env - ls -la - cd wp-content/plugins/foo-setup - phpcs . - cd - - cd wp-content/themes/foo - phpcs --ignore=bootstrap,inc,js,css . - cd - unit_tests: script: - cd wp-content/plugins/foo-setup - WP_TESTS_DIR=/opt/wp-tests/wp-tests-lib phpunit
  10. GitLab CI code_lint: script: - date - uname -a -

    env - ls -la - cd wp-content/plugins/foo-setup - phpcs . - cd - - cd wp-content/themes/foo - phpcs --ignore=bootstrap,inc,js,css . - cd - unit_tests: script: - cd wp-content/plugins/foo-setup - WP_TESTS_DIR=/opt/wp-tests/wp-tests-lib phpunit
  11. Puppet exec { '/bin/echo root >> /usr/lib/cron/cron.allow': path => '/usr/bin:/usr/sbin:/bin',

    unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null', } exec { 'tar -xf /Volumes/nfs02/important.tar': cwd => '/var/tmp', creates => '/var/tmp/myfile', path => ['/usr/bin', '/usr/sbin',], }
  12. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"]
  13. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"]
  14. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"]
  15. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"] cp -r . /app cd /app
  16. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"]
  17. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"] export DEBIAN_FRONTEND="noninteractive" export PATH="/app:$PATH" export ZZPATH="/app/funcoeszz" export ZZDIR="/app/zz"
  18. Docker FROM debian:jessie ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get

    install -y curl COPY . /app WORKDIR /app ENV PATH /app:$PATH ENV ZZPATH /app/funcoeszz ENV ZZDIR /app/zz ENTRYPOINT ["bash", "funcoeszz"] CMD ["--help"] if test $# -gt 0 then bash funcoeszz "$@" else bash funcoeszz --help fi
  19. Explore o ambiente $ date $ uname -a $ env

    | sort $ whoami $ groups $ ls -la $ ps auxw $ git branch $ git status $ docker info $ docker ps
  20. Use funções para organizar o script #!/bin/bash build() { docker

    build ... } publish() { docker push ... docker rmi ... } run() { docker run ... } build publish run
  21. Crie uma pasta “scripts” no repositório $ ls -1 scripts/

    _lib.sh build.sh cleanup.sh database-reset.sh deploy.sh merge-branch.sh publish.sh test.sh $
  22. Pratique POSIX #!/bin/sh Evite arrays Evite ${variavel/isso/aquilo} Evite ${variavel:1:3} Evite

    [[ Evite $FUNCNAME Evite for(;;) Evite $'…' Evite comando < <(comando)
  23. Seja --verbose, mande tudo para STDOUT echo "Iniciando em $(date)"

    echo "Copiando os arquivos para $destino" cp -v * "$destino" echo echo "Iniciando o backup" tar cvzf meuapp-$timestamp.tgz "$destino" echo "Finalizando em $(date)"
  24. Nomenclatura de variáveis Maiúsculas para variáveis de ambiente: $PATH $BUILD_ID

    $TRAVIS_OS_NAME Minúsculas para variáveis do script: base_dir=$(pwd) base_url='http://example.com' use_colors=1 Minúsculas para variáveis locais de funções: local i=0 local branch local path
  25. Conheça as variáveis do seu ambiente Jenkins GitLab CI Travis

    $BUILD_ID $BUILD_NUMBER $BUILD_URL $JOB_NAME $GIT_COMMIT $CI_BUILD_ID $CI_BUILD_NAME $CI_BUILD_STAGE $CI_SERVER_NAME $CI_PROJECT_DIR $TRAVIS_BUILD_NUMBER $TRAVIS_JOB_NUMBER $TRAVIS_COMMIT $TRAVIS_OS_NAME $TRAVIS_PYTHON_VERSION
  26. Use variáveis de ambiente $ ./meu-script.sh meu-app master 123 $

    REPO=meu-app BRANCH=master VERSION=132 ./meu-script.sh
  27. Use o modo “strict” do bash #!/bin/bash # Modo strict

    set -euo pipefail # set -e para abortar o script se houver qualquer erro cp /etc/passwd # set -u para tornar erro acessar variável não existente echo "$foo" # set -o pipefail para ajustar o exit code de um pipeline grep root /etc/passssswd | sort
  28. Use o modo “strict” do bash (com exceções) #!/bin/bash #

    Modo strict set -euo pipefail # set -e para abortar o script se houver qualquer erro cp /etc/passwd || true # set -u para tornar erro acessar variável não existente echo "${foo:-}" # set -o pipefail para ajustar o exit code de um pipeline grep root /etc/passssswd | sort
  29. Ligue o debug com $DEBUG #!/bin/bash test -n "$DEBUG" &&

    set -x Para executar: $ ./meu-script.sh # sem debug $ DEBUG=1 ./meu-script.sh # com debug
  30. Ligue o debug com $DEBUG - strict #!/bin/bash set -euo

    pipefail test -n "${DEBUG:-}" && set -x
  31. shellcheck http://www.shellcheck.net $ shellcheck meu-script.sh Line 3: for f in

    $(ls *.txt) ^-- SC2045: Iterating over ls output is fragile. Line 5: grep -i nofx.*mp3 $f ^-- SC2062: Quote the grep pattern so the shell won't interpret it. ^-- SC2086: Double quote to prevent globbing and word splitting.
  32. jq - Parser de JSON https://stedolan.github.io/jq/ $ cat foo.json {

    "foo": { "bar": { "baz": 123 } } } $ cat foo.json | jq . { "foo": { "bar": { "baz": 123 } } } $ cat foo.json | jq .foo.bar.baz 123 $
  33. curl, sed $ curl --location -D headers.txt http://www.example.com $ sed

    -i "s/%hostname%/$(hostname)/" /etc/app.conf $ sed -i '/debug/ s/false/true/' /etc/app.conf $ docker exec meu-app sed -i '/debug/ s/false/true/' /etc/app.conf
  34. Cloud CLI AWS https://aws.amazon.com/cli/ aws Azure https://github.com/Azure/azure-xplat-cli azure DigitalOcean https://github.com/digitalocean/doctl

    doctl Google Cloud https://cloud.google.com/sdk/ gcloud, gsutil, bq, kubectl IBM Bluemix e Cloud Foundry https://clis.ng.bluemix.net bluemix, cf Rackspace https://developer.rackspace.com/docs/rack-cli/ rack