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

Making Things Happen With Make

Making Things Happen With Make

#phptek 2023

Joe Ferguson

May 23, 2023
Tweet

More Decks by Joe Ferguson

Other Decks in Programming

Transcript

  1. Making Things Happen With Make
    Joe Ferguson - May 18th, 2023

    View full-size slide

  2. Joe Ferguson
    Principal Software Engineer @
    Preteckt
    Open Source Geek
    OSMI fanboi
    ⚽,🏒, and 🏎 fan
    Twitter: @JoePFerguson

    View full-size slide

  3. Remember Twitter? 😂

    View full-size slide

  4. `$ man make` 📚
    DESCRIPTION
    The purpose of the make utility is to determine
    automatically which pieces of a large program
    need to be recompiled, and issue the commands to
    recompile them.

    View full-size slide

  5. Make is old enough to be cool again… again
    1976

    View full-size slide

  6. Recompile only what needs it

    View full-size slide

  7. Compile a simple C application with make
    foo: foo.c
    cc foo.c -o foo
    // foo.c
    int main() { return 0; }

    View full-size slide

  8. Because we used a prerequisite we only run make

    View full-size slide

  9. But why though?

    View full-size slide

  10. Make complex things simple

    View full-size slide

  11. php-src uses Make to build PHP

    View full-size slide

  12. Laravel uses Makefiles to build Vapor Docker images

    View full-size slide

  13. Ansible uses Make to build documentation

    View full-size slide

  14. Make for PHP developers Part 1 - composer.json
    "scripts": {
    "post-autoload-dump": [
    "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
    "@php artisan package:discover --ansi",
    "@php artisan vendor:publish --force --tag=livewire:assets --ansi"
    ],
    "post-create-project-cmd": [
    "php artisan key:generate"
    ]
    },

    View full-size slide

  15. Make for PHP developers Part 2 package.json
    "scripts": {
    "dev": "npm run development",
    "development": "mix",
    "watch": "mix watch",
    "watch-poll": "mix watch -- --watch-options-poll=1000",
    "hot": "mix watch --hot",
    "prod": "npm run production",
    "production": "mix --production"
    },

    View full-size slide

  16. Anatomy of Make targets
    target: prerequisites
    command
    command
    command

    View full-size slide

  17. How do you setup your development environment?
    $ cp .env.example .env
    $ composer install # must be tab indent
    $ npm install
    $ npm run development
    $ mysql -u root -e "CREATE DATABASE IF NOT EXISTS app;"
    $ php artisan key:generate
    $ php artisan migrate

    View full-size slide

  18. Enter our Makefile
    .DEFAULT_GOAL := setup # make by itself will run setup
    .PHONY: setup
    setup: # this is the target
    cp .env.example .env # must be tab indent
    composer install # there are no PSRs here
    npm install
    php artisan key:generate
    php artisan migrate

    View full-size slide

  19. make setup
    The setup target runs our
    commands in the order we
    specified.

    View full-size slide

  20. How do you destroy your development environment?
    .PHONY: clean
    clean:
    rm -rf vendor/
    rm -rf node_modules/

    View full-size slide

  21. make clean
    Our vendor and node_modules
    folders will be removed.

    View full-size slide

  22. Adding Makefile to existing Projects

    View full-size slide

  23. Running the Test Suite

    View full-size slide

  24. Build our initial Makefile in the project root
    .DEFAULT_GOAL := setup
    .PHONY: setup
    setup:
    cp .env.example .env
    composer install
    npm install
    npm run development
    php artisan key:generate
    php artisan migrate
    .PHONY: clean
    clean:
    rm -rf vendor/
    rm -rf node_modules/

    View full-size slide

  25. Adding a test target
    .PHONY: test
    test:
    php vendor/bin/phpunit

    View full-size slide

  26. Running the Test Suite

    View full-size slide

  27. Adding test_browser target
    .PHONY: test_browser
    test_browser:
    cp .env.dusk.example .env.dusk
    php artisan dusk tests/Browser

    View full-size slide

  28. Running a webserver and chromedriver

    View full-size slide

  29. Run browser tests with headless Chrome

    View full-size slide

  30. Adding a target to clean our database
    .PHONY: clean_db
    clean_db:
    php artisan migrate:fresh
    php artisan db:seed

    View full-size slide

  31. Using Make to reset our database to a known state

    View full-size slide

  32. Getting complicated with Variables

    View full-size slide

  33. Making strings and things
    SHELL := /bin/bash
    PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
    PROJECT_NAME = build_things_get_paid
    PYTHON_INTERPRETER= python3.11
    ENV_DIR = env

    View full-size slide

  34. Debugging your variables
    $(info SHELL="$(SHELL)")
    $(info PROJECT_DIR="$(PROJECT_DIR)")
    $(info PROJECT_NAME="$(PROJECT_NAME)")
    $(info PYTHON_INTERPRETER="$(PYTHON_INTERPRETER)")
    $(info ENV_DIR="$(ENV_DIR)")

    View full-size slide

  35. Debugging your variables
    SHELL="/bin/bash"
    PROJECT_DIR="/home/halo/Code/demo"
    PROJECT_NAME="build_things_get_paid"
    PYTHON_INTERPRETER="python3.11"
    ENV_DIR="env"

    View full-size slide

  36. Using variables in make targets
    sniff:
    ./vendor/bin/phpcs -h $(PROJECT_NAME)

    View full-size slide

  37. Clean up files wherever they may be in the project
    clean:
    find . -type f -name "*.py[co]" -delete
    find . -type d -name "__pycache__" -delete

    View full-size slide

  38. Adding Variables to our Makefile
    REGISTRY := registry.phparch.com
    REPOSITORY := svpernova09/snipe-it
    BUILD = DOCKER_BUILDKIT=1 docker build
    .DEFAULT_GOAL := setup

    View full-size slide

  39. Adding Variables to our Makefile
    .DEFAULT_GOAL := setup
    .PHONY: docker_build
    docker_build:
    $(BUILD) -t $(REGISTRY)/$(REPOSITORY):latest -f Dockerfile .

    View full-size slide

  40. Running make docker_build

    View full-size slide

  41. We’re using variables for string concatenation
    DOCKER_BUILDKIT=1 docker build -t
    registry.phparch.com/svpernova09/snipe-it:latest
    -f Dockerfile .

    View full-size slide

  42. Build Multiple Images
    .PHONY: docker_build_alpine
    docker_build_alpine:
    $(BUILD) -t $(REGISTRY)/$(REPOSITORY)-alpine:latest -f Dockerfile.alpine .
    .PHONY: docker_build_alpine_fpm
    docker_build_alpine_fpm:
    $(BUILD) -t $(REGISTRY)/$(REPOSITORY)-fpm-alpine:latest -f
    Dockerfile.fpm-alpine .

    View full-size slide

  43. Reuse targets to chain them together
    .PHONY: docker_build_all
    docker_build_all: | docker_build docker_build_alpine docker_build_alpine_fpm

    View full-size slide

  44. Running make docker_build_all builds all 3 images

    View full-size slide

  45. Don’t forget to publish them to your registry
    .PHONY: docker_push_all
    docker_push:
    docker push $(REGISTRY)/$(REPOSITORY):latest
    docker push $(REGISTRY)/$(REPOSITORY)-alpine:latest
    docker push $(REGISTRY)/$(REPOSITORY)-fpm-alpine:latest

    View full-size slide

  46. Use a known value for your build images
    BUILD = DOCKER_BUILDKIT=1 docker build
    ifndef GITHUB_RUN_ID
    GITHUB_RUN_ID=latest
    endif
    .DEFAULT_GOAL := setup

    View full-size slide

  47. Allows us to be more flexible in our tags
    .PHONY: docker_push_all
    docker_push:
    docker push $(REGISTRY)/$(REPOSITORY):$(GITHUB_RUN_ID)
    docker push $(REGISTRY)/$(REPOSITORY)-alpine:$(GITHUB_RUN_ID)
    docker push $(REGISTRY)/$(REPOSITORY)-fpm-alpine:$(GITHUB_RUN_ID)

    View full-size slide

  48. Use Make in your CICD

    View full-size slide

  49. Typical configuration to set up our app and run tests
    name: PHP App
    on: [push, pull_request]
    jobs:
    build:
    runs-on: ubuntu-20.04
    strategy:
    fail-fast: false
    matrix:
    php: ['8.1']
    name: PHP ${{ matrix.php }}
    services:
    mysql:
    image: mysql:5.7
    env:
    MYSQL_ALLOW_EMPTY_PASSWORD: yes
    MYSQL_DATABASE: app
    ports:
    - 3306
    options: --health-cmd="mysqladmin ping"
    --health-interval=10s --health-timeout=5s --health-retries=3

    View full-size slide

  50. Typical configuration to set up our app and run tests
    steps:
    - name: Checkout Code
    uses: actions/checkout@v1
    - name: Setup PHP
    uses: shivammathur/[email protected]
    with:
    php-version: ${{ matrix.php }}
    extensions: dom, curl, libxml, mbstring
    - name: Copy Env
    run: cp .env.example .env
    - name: Install dependencies & Setup App
    run: |
    composer install && php artisan key:generate --ansi &&
    php artisan migrate --force && php artisan db:seed
    --force
    env:
    DB_PORT: ${{ job.services.mysql.ports[3306] }}

    View full-size slide

  51. Run the tests!
    - name: Run the tests
    run: vendor/bin/phpunit
    env:
    DB_PORT: ${{ job.services.mysql.ports[3306] }}

    View full-size slide

  52. Replace commands with our make targets
    steps:
    - name: Checkout Code
    uses: actions/checkout@v1
    - name: Setup PHP
    uses: shivammathur/[email protected]
    with:
    php-version: ${{ matrix.php }}
    extensions: dom, curl, libxml, mbstring, zip, pcntl, ...
    coverage: none
    - name: Install dependencies & Setup App
    run: make setup
    env:
    DB_PORT: ${{ job.services.mysql.ports[3306] }}
    - name: Run tests
    run: make test
    env:
    DB_PORT: ${{ job.services.mysql.ports[3306] }}

    View full-size slide

  53. You can now easily replicate CICD

    View full-size slide

  54. Using Make to deploy our application
    .PHONY: deploy
    deploy:
    php vendor/bin/envoy run deploy

    View full-size slide

  55. Adding our SSH key to Secrets for Deployments
    - name: Install SSH key
    uses: shimataro/ssh-key-action@v2
    with:
    key: ${{ secrets.SSH_KEY }}
    known_hosts: ${{ secrets.KNOWN_HOSTS }}
    if: github.ref == 'refs/heads/master'
    - name: Deploy
    run: make deploy
    if: github.ref == 'refs/heads/master'

    View full-size slide