{"id":18084,"date":"2024-03-25T18:02:39","date_gmt":"2024-03-25T18:02:39","guid":{"rendered":"http:\/\/scannn.com\/laravel-continuous-integration-with-github-actions\/"},"modified":"2024-03-25T18:02:39","modified_gmt":"2024-03-25T18:02:39","slug":"laravel-continuous-integration-with-github-actions","status":"publish","type":"post","link":"https:\/\/scannn.com\/lv\/laravel-continuous-integration-with-github-actions\/","title":{"rendered":"Laravel Continuous Integration with GitHub Actions"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Working with frameworks like Laravel brings a plethora of features and efficiencies. These frameworks make major features like Dependency Injection, Real-time Events, Queuing systems, Mailing, Localization, Caching, and more simple to implement for developers. Not only that, but these frameworks can bring quality of life to local development too, with features such as local development containers (Laravel Sail), Testing Suites, starter kits, CLI Toolkits, and readily available packages to use in development and production environments. These robust features give developers the <em>power<\/em> to build scalable, enterprise-ready web applications with relative ease, but with great power comes great responsibility. <\/p>\n<ul>\n<li>How do you monitor all of these different features your web application uses? <\/li>\n<li>How do you ensure your squad of developers are writing code in an effective manner? <\/li>\n<li>How do you <em>measure<\/em> the productivity of a team working on these applications? <\/li>\n<li>How do you ensure code updates won\u2019t break any part of your application system? <\/li>\n<li>How do you ensure those developers run tests and deploy excellent code?<\/li>\n<\/ul>\n<p>While you or your team may have different answers to those questions, the common connection between these questions is the fact that <em>these processes can be automated<\/em>. That automation can materialize in different ways, and balancing the automation with your current workflow can seem daunting. Not only that, but if you have critical workloads and applications that are heavily and manually monitored throughout it\u2019s deployment lifecycle, it may seem like automating parts of that would introduce more points of failure. After reading through the Continuous Improvement and Continuous Integration processes that we have implemented at AdAction, we hope that you\u2019ll be more confident in the boons that CI can bring to you and your team.<\/p>\n<h2 id=\"GitHub-Actions-for-Laravel-Applications\">GitHub Actions for Laravel Applications<\/h2>\n<p>There are a few different tools for integrating Continuous Integration with Laravel, but we\u2019re going to focus primarily on GitHub Actions and their use with Laravel. With that said, many of these integrations can be \u201ceasily\u201d ported over to whatever tooling you desire, as long as you can figure the configuration part out.<\/p>\n<p>Also, keep in mind, the requirements to integrate these tools and tests we\u2019re running may be different than the requirements to run the application itself.<\/p>\n<h4 id=\"Requirements\">Requirements<\/h4>\n<ul>\n<li>PHP (We like to use phpbrew for managing different PHP versions on our local machines)<\/li>\n<li>Composer<\/li>\n<li>Node (We like to use nvm for managing different Node version on our local machines)<\/li>\n<li>npm<\/li>\n<li>Laravel<\/li>\n<li>GitHub Actions We\u2019ll walk through integrating\/installing this together.<\/li>\n<\/ul>\n<h3 id=\"Install-example-application\">Install example application<\/h3>\n<p>If you\u2019d like to see how these workflows look like in an actual project, you may locally clone our example repo to follow along. https:\/\/github.com\/AdAction\/articles-laravel-continuous-integration. You may also view the GitHub Actions execution results directly from that repository.<\/p>\n<h3 id=\"Installing-GitHub-Actions\">Installing GitHub Actions<\/h3>\n<p>If you haven\u2019t already, let\u2019s get GitHub Actions integrated into our project! The only requirement to integrate GitHub Actions with your project is for that project repository to be a GitHub Repository. After that, adding new GitHub Workflows is as simple as adding new files in that projects <code>.github\/workflows<\/code> directory.<\/p>\n<h2 id=\"Web-Application-Use-Cases\">Web Application Use Cases<\/h2>\n<p>The following examples aren\u2019t necessarily Laravel specific, and are common use-cases amongst all web applications. For the sake of this article, though, we\u2019re going to be writing these in the context of a Laravel Application. We\u2019ve presented these use-cases in order of operations, where the first of theses use-cases are being run earlier in the SDLC, and the latter ones being run later.<\/p>\n<h3 id=\"Building\">Building<\/h3>\n<p>We want to ensure, at the very least, our application code can be built. For Laravel applications, that means installing the dependencies and running <code>npm run build<\/code>.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Build  \n  \nconcurrency:  \n  group: ${{ github.workflow }}-${{ github.ref }}  \n  cancel-in-progress: true  \n  \non:  \n  workflow_call:  \n  pull_request:  \n  \nenv:  \n  EXTENSIONS: pcntl, bcmath, zip, gd, sodium, :php-psr  \n  PHP_VERSION: \"8.2\"  \n  NODE_VERSION: 20  \n  \njobs:  \n  build-application:  \n    runs-on: ubuntu-latest-m  \n  \n    steps:  \n    - name: Checkout code  \n      uses: actions\/checkout@v4  \n  \n    - name: Setup PHP  \n      uses: shivammathur\/setup-php@v2  \n      with:  \n        php-version: ${{ env.PHP_VERSION }}  \n        extensions: ${{ env.EXTENSIONS }}  \n        tools: composer, pecl  \n        coverage: xdebug  \n  \n    - name: Install Deps  \n      uses: \"ramsey\/composer-install@v3\"    \n  \n\t- name: Setup node  \n\t  uses: actions\/setup-node@v4  \n\t  with:  \n\t    node-version: ${{ env.NODE_VERSION }}  \n\t    cache: \"npm\"\n  \n  \n<\/pre>\n<\/div>\n<h3 id=\"Formatting-and-Styling\">Formatting and Styling<\/h3>\n<p>Code styling and formatting are important for a few different reasons. Having a common code style configuration for all developers to use allows developers to focus their attentions on stuff that can\u2019t be automated and easily checked and tested. Instead of taking a non-trivial amount of time discussing and reviewing code styles for updates, a common format and style can be applied to code intermittently during development, as well as automatically in Pull Requests. See the Wikipedia on the Law of Triviality.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Linting  \n  \non:  \n  workflow_call:  \n  pull_request:  \n  \nenv:  \n  COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}  \n  EXTENSIONS: pcntl, bcmath, zip, gd, sodium, :php-psr  \n  PHP_VERSION: \"8.2\"  \n  \njobs:  \n  php-formatting-and-styling:  \n    runs-on: ubuntu-latest  \n    needs: build  \n    concurrency:  \n      group: release  \n  \n    steps:  \n      - name: Checkout  \n        uses: actions\/checkout@v4  \n  \n      - name: Setup PHP  \n        uses: shivammathur\/setup-php@v2  \n        with:  \n          php-version: ${{ env.PHP_VERSION }}  \n          extensions: ${{ env.EXTENSIONS }}  \n          tools: composer, pecl  \n          coverage: xdebug  \n  \n      - name: Install Deps  \n        uses: \"ramsey\/composer-install@v3\"  \n  \n      - name: Check Code Styles  \n        run: \"vendor\/bin\/pint --test\"  \n  \n  node-formatting-and-styling:  \n    runs-on: ubuntu-latest  \n    needs: build  \n    concurrency:  \n      group: release  \n  \n    steps:  \n      - name: Checkout  \n        uses: actions\/checkout@v4  \n  \n      - name: Setup node  \n        uses: actions\/setup-node@v4  \n        with:  \n          node-version: ${{ env.NODE_VERSION }}  \n          cache: \"npm\"  \n  \n      - name: Build App  \n        run: npm ci  \n  \n      - name: Check Code Styles  \n        run: \"npm run prettier\"\n<\/pre>\n<\/div>\n<h3 id=\"Static-Analysis\">Static Analysis<\/h3>\n<p>Different from Formatting and Styling, Static Analysis tells us that our code is written <em>efficiently<\/em>. For example, does the code have unused variables, obvious memory leaks, hanging processes, circular references, etc. The configuration used to report these issues can be altered to reflect how strictly you want to analyze your code, and you can even create a baseline file for integrating Static Analysis in an existing project to enable analysis on all future files, while giving your team time to handle the existing issues separately.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Linting  \n  \non:  \n  workflow_call:  \n  pull_request:  \n  \nenv:  \n  COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}  \n  EXTENSIONS: pcntl, bcmath, zip, gd, sodium, :php-psr  \n  PHP_VERSION: \"8.2\"  \n  \njobs:  \n  php-linting:  \n    runs-on: ubuntu-latest  \n    needs: build  \n    concurrency:  \n      group: release  \n  \n    steps:  \n      - name: Checkout  \n        uses: actions\/checkout@v4  \n  \n      - name: Setup PHP  \n        uses: shivammathur\/setup-php@v2  \n        with:  \n          php-version: ${{ env.PHP_VERSION }}  \n          extensions: ${{ env.EXTENSIONS }}  \n          tools: composer, pecl  \n          coverage: xdebug  \n  \n      - name: Install Deps  \n        uses: \"ramsey\/composer-install@v3\"  \n  \n      - name: Check Code Styles  \n        run: \"vendor\/bin\/pint --test\"  \n  \n      - name: Setup problem matchers for PHP  \n        run: echo \"::add-matcher::${{ runner.tool_cache }}\/php.json\"  \n  \n      - name: Run PHPStan  \n        run: vendor\/bin\/phpstan analyse --no-progress  \n  \n  node-linting:  \n    runs-on: ubuntu-latest  \n    needs: build  \n    concurrency:  \n      group: release  \n  \n    steps:  \n      - name: Checkout  \n        uses: actions\/checkout@v4  \n  \n      - name: Setup node  \n        uses: actions\/setup-node@v4  \n        with:  \n          node-version: ${{ env.NODE_VERSION }}  \n          cache: \"npm\"  \n  \n      - name: Build App  \n        run: npm ci  \n  \n      - name: Check Code Styles  \n        run: \"npm run prettier\"  \n  \n      - name: Run ESLint  \n        run: npx eslint . --ext .vue,.js\n<\/pre>\n<\/div>\n<h3 id=\"Testing-and-Code-Coverage\">Testing and Code Coverage<\/h3>\n<p>We have a large amount of tests to run, for both our PHP and JS\/Vue part of our Application. We want those tests to be run for different occasions. We want to run our tests when PRs are opened so reviewers can more easily know that what they\u2019re reviewing is quality code. We also want to run these tests before creating new Builds and Releases. We have different suites for Unit, Feature, and Integration tests. The Code Coverage test tells us if an update strengthens or weakens our overall tests, and tells us if the update itself is well tested. Pick a code coverage threshold that fits your team\u2019s existing testing structure, don\u2019t try to enforce un-enforceable requirements that will hinder your deployment process.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Test  \n  \nconcurrency:  \n  group: ${{ github.workflow }}-${{ github.ref }}  \n  cancel-in-progress: true  \n  \non:  \n  workflow_call:  \n  pull_request:  \n  \nenv:  \n  COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}  \n  EXTENSIONS: pcntl, bcmath, zip, gd, sodium, :php-psr  \n  PHP_VERSION: \"8.2\"  \n  NODE_VERSION: 20  \n  \njobs:  \n  php-tests:  \n    runs-on: ubuntu-latest-m  \n  \n    steps:  \n    - name: Checkout code  \n      uses: actions\/checkout@v4  \n  \n    - name: Setup PHP  \n      uses: shivammathur\/setup-php@v2  \n      with:  \n        php-version: ${{ env.PHP_VERSION }}  \n        extensions: ${{ env.EXTENSIONS }}  \n        tools: composer, pecl  \n        coverage: xdebug  \n  \n    - name: Install Deps  \n      uses: \"ramsey\/composer-install@v3\"  \n  \n    - name: Initialize Project  \n      run: |  \n        cp .env.ci .env  \n        composer app:init  \n  \n    - name: Run PHP Unit Tests  \n      run: php artisan test --ci --coverage-clover=coverage.xml --log-junit=test-report.xml  \n  \n  js-tests:  \n    runs-on: ubuntu-latest  \n  \n    steps:  \n    - name: Checkout code  \n      uses: actions\/checkout@v4  \n  \n    - name: Setup node  \n      uses: actions\/setup-node@v4  \n      with:  \n        node-version: ${{ env.NODE_VERSION }}  \n        cache: \"npm\"  \n  \n    - name: Build App  \n      run: npm ci  \n  \n    - name: Run CDK Tests  \n      run: npm test\n<\/pre>\n<\/div>\n<h3 id=\"Releasing\">Releasing<\/h3>\n<p>On pushes to our main branch, we want to automate releases to our GitHub repo. These releases are used to trigger another workflow that deploys the release to an S3 Bucket (which then triggers a deployment from CodePipeline, but that\u2019s beyond the scope of this Article). These releases follow Semantic Versioning, which help keep our dependency management cleaner and Software Development Lifecycle easier to follow along with on a timeline. Semantic versioning is automated by the use of an npm package called <code>semantic-release<\/code>.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Release  \n  \non:  \n  push:  \n    branches:  \n    - main  \n  \njobs:  \n  semantic-release:  \n    runs-on: ubuntu-latest  \n  \n    steps:  \n      - name: Checkout  \n        uses: actions\/checkout@v4  \n        with:  \n          fetch-depth: 0  \n          token: ${{ secrets.PUBLIC_REPO_BOT_GITHUB_TOKEN }}  \n  \n      - name: Setup Node.js  \n        uses: actions\/setup-node@v4  \n        with:  \n          node-version: \"lts\/*\"  \n  \n      - name: Install semantic-release  \n        run: |  \n          npm install -g semantic-release@21  \n          npm install -g @semantic-release\/changelog  \n          npm install -g @semantic-release\/git  \n  \n      - name: Install Deps  \n        uses: \"ramsey\/composer-install@v3\"  \n  \n      - name: Release  \n        env:  \n          GITHUB_TOKEN: ${{ secrets.PUBLIC_REPO_BOT_GITHUB_TOKEN }}  \n          SLACK_TOKEN: ${{ secrets.PUBLIC_REPO_SLACK_TOKEN }}  \n          GIT_AUTHOR_NAME: ${{ vars.PUBLIC_BOT_USERNAME }}  \n          GIT_AUTHOR_EMAIL: ${{ vars.PUBLIC_BOT_EMAIL }}  \n          GIT_COMMITTER_NAME: ${{ vars.PUBLIC_BOT_USERNAME }}  \n          GIT_COMMITTER_EMAIL: ${{ vars.PUBLIC_BOT_EMAIL }}  \n        run: npx semantic-release\n<\/pre>\n<\/div>\n<h3 id=\"Integrating-3rd-Party-Tools\">Integrating 3rd Party Tools<\/h3>\n<p>We use a few different 3rd party tools in our projects, such as DataDog, MaxMind GeoIP, and DevCycle. Most of these tools require installation outside of the PHP Ecosystem, meaning files and commands need to be installed and external resources configured. We currently have a GitHub Workflow for automatically displaying code snippets for each DevCycle variable usage within our project.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: DevCycle Usages  \n  \non:  \n  push:  \n    branches: [main]  \n  \nenv:  \n  DVC_PROJECT_KEY: \"test_project_key\"  \n  \njobs:  \n  dvc-code-usages:  \n    runs-on: ubuntu-latest  \n    name: DevCycle Feature Flag Code Usages  \n    steps:  \n      - uses: actions\/checkout@v4  \n        with:  \n          fetch-depth: 0  \n      - uses: DevCycleHQ\/feature-flag-code-usage-action@v1.1.2  \n        with:  \n          github-token: ${{ secrets.GITHUB_TOKEN }}  \n          client-id: ${{ secrets.DVC_CLIENT_ID }}  \n          client-secret: ${{ secrets.DVC_CLIENT_SECRET }}  \n          project-key: ${{ env.DVC_PROJECT_KEY }}\n<\/pre>\n<\/div>\n<h3 id=\"Deploying\">Deploying<\/h3>\n<p>When a Release has been made in our project, another Workflow gets triggered that deploys our new release to an S3 bucket. Though our method of deployment is straightforward from the GitHub Actions side, a more involved process may be required depending on your deployment needs.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Deploy\n\nconcurrency: deployment\n\non:\n  release:\n    types: [published]\n\npermissions:\n  id-token: write   # This is required for requesting the JWT\n  contents: read    # This is required for actions\/checkout\n  pull-requests: write\n\njobs:\n  upload-release:\n    runs-on: ubuntu-latest\n\n    steps:\n\n    - name: Set start time\n      run: |\n        START_TIME=$(date +%s) \n        echo \"START_TIME=$START_TIME\" &gt;&gt; $GITHUB_ENV\n\n    - name: configure aws credentials\n      uses: aws-actions\/configure-aws-credentials@v3\n      with:\n        role-to-assume: arn:aws:iam::172122050326:role\/PublicGitHubRepoOIDCRole\n        role-session-name: DeployGitHubActions\n        aws-region: us-east-2\n\n    - name: Checkout code\n      uses: actions\/checkout@v4\n\n    - name: Zip files\n      run: |\n        zip -r laravel-app-latest.zip .\n\n    - name: Copy file to S3 with metadata\n      env:\n        AWS_REGION: us-east-2\n      run: |\n        aws s3 cp --metadata github-sha=${{ github.sha }},start-time=${START_TIME} .\/laravel-app-latest.zip s3:\/\/articles-laravel-continuous-integration\/laravel-app-latest.zip\n<\/pre>\n<\/div>\n<h3 id=\"Cleanup-and-Efficiency\">Cleanup and Efficiency<\/h3>\n<p>Having the build, lint, and test workflows separate is a waste of resources, lets combine them to save a bit of time and money!<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nname: Build\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  workflow_call:\n  pull_request:\n\nenv:\n  COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}\n  EXTENSIONS: pcntl, bcmath, zip, gd, sodium, :php-psr\n  PHP_VERSION: \"8.2\"\n  NODE_VERSION: 20\n\njobs:\n  build-lint-and-test:\n    runs-on: ubuntu-latest\n    services:\n      mysql:\n        image: mysql\n\n        env:\n          MYSQL_DATABASE: articles-laravel-ci\n          MYSQL_USER: exampleuser\n          MYSQL_PASSWORD: examplepassword\n          MYSQL_ROOT_PASSWORD: examplepassword\n\n        ports:\n          - 33306:3306\n\n        options: --health-cmd=\"mysqladmin ping\" --health-interval=10s --health-timeout=5s --health-retries=3\n\n    steps:\n    - name: Checkout code\n      uses: actions\/checkout@v4\n\n    - name: Setup PHP\n      uses: shivammathur\/setup-php@v2\n      with:\n        php-version: ${{ env.PHP_VERSION }}\n        extensions: ${{ env.EXTENSIONS }}\n        tools: composer, pecl\n        coverage: xdebug\n\n    - name: Install Deps\n      uses: \"ramsey\/composer-install@v3\"\n\n    - name: Setup node\n      uses: actions\/setup-node@v4\n      with:\n        node-version: ${{ env.NODE_VERSION }}\n        cache: \"npm\"\n\n    - name: Build App\n      run: npm ci\n\n    - name: Check Code Styles\n      run: \"vendor\/bin\/pint --test\"\n\n    - name: Setup problem matchers for PHP\n      run: echo \"::add-matcher::${{ runner.tool_cache }}\/php.json\"\n\n    - name: Run PHPStan\n      run: vendor\/bin\/phpstan analyse --no-progress\n\n    - name: Initialize Project\n      run: |\n        cp .env.example .env\n        php artisan key:generate\n        php artisan migrate --seed\n\n    - name: Run PHP Unit Tests\n      run: php artisan test\n\n    - name: Check Code Styles\n      run: \"npm run prettier\"\n\n    - name: Run ESLint\n      run: npm run lint\n\n    - name: Run CDK Tests\n      run: npm test\n\n<\/pre>\n<\/div>\n<h2 id=\"Future-Use-Cases\">Future Use Cases<\/h2>\n<h3 id=\"Integrating-reusable-workflows\">Integrating reusable workflows<\/h3>\n<p>We have quite a few repeated uses of our dependency installations. Putting that in a separate <code>build<\/code> workflow, with all the others referencing it is in the plans too for our future, so look out for that article too!<\/p>\n<h3 id=\"Caching-dependencies\">Caching dependencies<\/h3>\n<p>We install dependencies for node and dependencies for php through <code>npm<\/code> and <code>composer<\/code>. We do it multiple times throughout our different workflows, and it\u2019s not efficient. What we can do instead is use GitHub\u2019s dependency caching integration. That would save us a significant time spent on jobs, but there are caching management concerns to make this an easy task.<\/p>\n<h3 id=\"Database-Migrations\">Database Migrations<\/h3>\n<p>Sometimes our code changes necessitate changes to our DB Schema as well. Right now, we run Database Migrations on every successful deployment, but sometimes, those Schema changes don\u2019t run properly in a non-local environment, and they cause issues in development or production environments. To combat this, we\u2019ve been planning out an update to our CI\/CD process where Database Migrations are run before the code deployment actually happens, and would alert us if any bad schema changes happen. This would take careful planning on our teams part; Ensuring code changes are precipitated by their required schema changes is difficult, but a well oiled team with strong communication and planning can handle that.<\/p>\n<h3 id=\"File-manipulation-and-preparation\">File manipulation and preparation<\/h3>\n<p>During our Elastic Beanstalk Deployment, we make changes to the codebase to ensure that the project can be run on the target environment. This includes changing permissions, setting up log files, adding folders, installing and updating tools, etc. This logic is stored in AWS EB Platform Hooks, but we want to move <em>project<\/em> changes to GitHub Actions, while keeping <em>environment<\/em> changes in those Platform Hooks.<\/p>\n<h2>Elevating Laravel with GitHub Actions<\/h2>\n<p>Implementing CI with Laravel using GitHub Actions presents a transformative opportunity for web developers, from setting up your environment with PHP, Composer, and Node.js, to integrating third-party tools for a seamless development workflow. By automating processes such as testing, building, and deploying, developers can ensure their code is both efficient and robust, minimizing the risk of errors and enhancing the overall quality of their applications. Future prospects, like deploying directly to Elastic Beanstalk and refining database migrations, highlight the evolving landscape of CI\/CD processes. <\/p>\n<p>At AdAction, as we continue embracing these technological advancements, the opportunities to streamline our development processes, enhance the quality of our code, and speed up our project timelines are vast. Integrating GitHub Actions into our Laravel projects not only improves our development methodologies but also ensures our team is aligned with the pursuit of more innovative, dependable, and scalable web applications.<\/p>\n<div class=\"saboxplugin-wrap\" itemtype=\"http:\/\/schema.org\/Person\" itemscope=\"\" itemprop=\"author\">\n<div class=\"saboxplugin-tab\">\n<div class=\"saboxplugin-gravatar\"><img decoding=\"async\" width=\"100\" height=\"100\" alt=\"Dakota Washok\" itemprop=\"image\" src=\"https:\/\/www.adaction.com\/wp-content\/uploads\/2024\/03\/dakota-w.jpeg\" class=\"lazyload\" bad-src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\"\/><noscript><img decoding=\"async\" src=\"https:\/\/www.adaction.com\/wp-content\/uploads\/2024\/03\/dakota-w.jpeg\" width=\"100\" height=\"100\" alt=\"Dakota Washok\" itemprop=\"image\"\/><\/noscript><\/div>\n<div class=\"saboxplugin-desc\">\n<div itemprop=\"description\">\n<p>Dakota is a Software Engineer with over ten years of coding experience, with expertise in a broad range of development technologies. Historically he\u2019s served in web development roles, but lately he\u2019s been optimizing everything he can in a DevOps position, where he can flex his analytical skills.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div><\/div>\n<p><br \/>\n<br \/><a href=\"https:\/\/www.adaction.com\/blog\/laravel-continuous-integration-with-github-actions\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Working with frameworks like Laravel brings a plethora of features and efficiencies. These frameworks make major features like Dependency Injection, Real-time Events, Queuing systems, Mailing, Localization, Caching, and more simple to implement for developers. Not only that, but these frameworks can bring quality of life to local development too, with features such as local development [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":18085,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[122],"tags":[],"class_list":["post-18084","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data"],"_links":{"self":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/18084","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/comments?post=18084"}],"version-history":[{"count":0,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/18084\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media\/18085"}],"wp:attachment":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media?parent=18084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/categories?post=18084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/tags?post=18084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}