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 Slide

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

    View Slide

  3. Remember Twitter? 😂

    View 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 Slide

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

    View Slide

  6. Recompile only what needs it

    View Slide

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

    View Slide

  8. Because we used a prerequisite we only run make

    View Slide

  9. But why though?

    View Slide

  10. Make complex things simple

    View Slide

  11. php-src uses Make to build PHP

    View Slide

  12. Laravel uses Makefiles to build Vapor Docker images

    View Slide

  13. Ansible uses Make to build documentation

    View 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 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 Slide

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

    View 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 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. Adding Makefile to existing Projects

    View Slide

  23. Running the Test Suite

    View 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 Slide

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

    View Slide

  26. Running the Test Suite

    View Slide

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

    View Slide

  28. Running a webserver and chromedriver

    View Slide

  29. Run browser tests with headless Chrome

    View Slide

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

    View Slide

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

    View Slide

  32. Getting complicated with Variables

    View 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 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 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 Slide

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

    View 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 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 Slide

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

    View Slide

  40. Running make docker_build

    View 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 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 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 Slide

  44. Running make docker_build_all builds all 3 images

    View 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 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 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 Slide

  48. Use Make in your CICD

    View 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 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 Slide

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

    View 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 Slide

  53. You can now easily replicate CICD

    View Slide

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

    View 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 Slide

  56. Thanks!
    Email: [email protected]

    View Slide