{"id":402,"date":"2024-07-21T03:03:36","date_gmt":"2024-07-21T10:03:36","guid":{"rendered":"https:\/\/kdcinfo.com\/blog\/?p=402"},"modified":"2024-07-21T03:03:40","modified_gmt":"2024-07-21T10:03:40","slug":"preliminary-adventures-in-ci-cd-with-flutter","status":"publish","type":"post","link":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/","title":{"rendered":"Preliminary Adventures in CI\/CD with Flutter"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In an effort to become a more well-rounded and full(er) stack developer, the time has come for me to embark on a path to understanding the world of CI\/CD within the realm of Flutter development.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I had, admittedly, put off learning CI\/CD because, while I looked forward to automating the development and deployment processes, immersing myself into the depths of such tangled tech was a tad intimidating for a greenish mobile dev. So I opted to procrastinate by spending a year refactoring two of my apps from <a href=\"https:\/\/pub.dev\/packages\/provider\" target=\"_blank\" rel=\"noreferrer noopener\">Provider<\/a> to <a href=\"https:\/\/pub.dev\/packages\/flutter_bloc\" target=\"_blank\" rel=\"noreferrer noopener\">Bloc<\/a>, then creating a <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/tree\/main\/tic-tac-tuple\" target=\"_blank\" rel=\"noreferrer noopener\">fully functional practical prototype<\/a>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Procrastination complete . . .<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-text-align-center wp-block-paragraph\"><strong><em>The time has come to learn CI\/CD.<\/em><\/strong><\/p>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"> <img loading=\"lazy\" decoding=\"async\" width=\"170\" height=\"96\" class=\"wp-image-417\" style=\"width: 170px;\" src=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/yoda-star-wars-ezgif.com-resize.gif\" alt=\"Learning Am I - Yoda\"><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Initial research revealed I had to look at CI\/CD from two perspectives: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>setting up <strong>development environments<\/strong>, and <\/li>\n\n\n\n<li>setting up <strong>deployment flows<\/strong>.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Initial observations also lead me to conclude:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>I finally get a <strong>practical opportunity to learn Docker<\/strong>. \ud83d\udc4f<\/li>\n\n\n\n<li>As imagined, there is <strong>no shortage of deployment approaches and tools<\/strong> to choose from.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">I next realized I had to determine which of my four Flutter apps would fall into which CI\/CD tool or tech approach. There is <strong>one open source app<\/strong> in the <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/tree\/main\/tic-tac-tuple\" target=\"_blank\" rel=\"noreferrer noopener\">&#8216;Dev Play&#8217; repository<\/a>, and there are <strong>three private apps<\/strong> related to <a href=\"https:\/\/kdrecall.com\" target=\"_blank\" rel=\"noreferrer noopener\">KD-reCall<\/a>, which is a small suite of simple reminder apps available in both app stores. Here&#8217;s the breakdown I ended up with.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Dev Play Flutter app<\/strong>: Open source =&gt; <code>Deployment<\/code> [web]<\/li>\n\n\n\n<li><strong>KD-reCall Flutter apps<\/strong>: Private =&gt; <code>Deployment<\/code> [android, ios]<\/li>\n\n\n\n<li><strong>KD-reCall APIs<\/strong>: Private =&gt; <code>Development<\/code> via Docker<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading bottom-border-separator\">Development with Docker<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I spent some time setting up a development image for API development, but have yet to try it out as I got busy setting up my first deployment flow. I trust I should have no issues with the development environment image as it really is just a modularized Virtual Machine (like <a href=\"https:\/\/www.virtualbox.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">VirtualBox<\/a>), and I&#8217;m already familiar with shelling into servers thanks to web hosting. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Although I know it&#8217;s been around forever, it was still great to see so many Docker images supporting so many environments and tools. For instance, having a MariaDB image in lieu of MySQL.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Draft Docker Image: Custom LAMP Development (untested)<\/summary>\n<pre class=\"wp-block-code\"><code># Mac: docker-compose\n\n## &gt; $ cd ~\/Development\/dev-play\/tic-tac-tuple\/\n\n## &gt; vi docker-compose.yml\n\n```yaml\nversion: '3.1'\nservices:\n    web:\n        image: nginx:latest\n        ports:\n            - \"8080:80\"\n            - \"443:443\"\n        environment:\n            - NGINX_HOST=kdcinfo.com\n            - NGINX_PORT=80\n        volumes:\n            - .\/nginx.conf:\/etc\/nginx\/conf.d\/nginx.conf # default.conf\n            - .\/docker\/nginx\/default.conf:\/etc\/nginx\/conf.d\/default.conf\n            - .:\/var\/www\/html # Web: kdcinfo\n    php:\n        # image: php:fpm\n        build:\n            context: .\n            dockerfile: PHP.Dockerfile\n        volumes:\n            - .:\/var\/www\/html\n    database:\n        # image: mariadb:latest\n        build:\n            dockerfile: docker\/database\/Dockerfile\n        ports:\n            - \"3306:3306\" # 3306:3306\n        environment:\n            #\n            # MYSQL_ROOT_PASSWORD: 'deepsecret'\n            # MYSQL_DATABASE: 'tutorialdb'\n            # MYSQL_USER: 'tutorial'\n            # MYSQL_PASSWORD: 'goodsecret'\n            MARIADB_ROOT_PASSWORD: 'deepsecret'\n            MARIADB_DATABASE: 'tutorialdb'\n            MARIADB_USER: 'tutorial'\n            MARIADB_PASSWORD: 'goodsecret'\n        volumes:\n            - database_data:\/var\/lib\/mysql\nvolumes:\n    # mysqldata: {}\n    database_data:\n        driver: local\n```\n\n- The `nginx.conf` file from the host is placed at `\/etc\/nginx\/conf.d\/nginx.conf` inside the container.\n- The `app` folder is created at the root of the container in `\/app`; place PHP scripts, images and JavaScript files.\n\n&gt; vi nginx.conf\n\n```yaml\nserver {\n    listen 80 default_server;\n    # root \/app\/public;\n    root \/app\/web;\n\n    index index.php index.html index.htm;\n\n    location ~ \\.php$ {\n        fastcgi_pass php:9000;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        include fastcgi_params;\n    }\n}\n```\n\n&gt; vi PHP.Dockerfile\n\n```yaml\nFROM php:fpm # php:8.2-fpm\n\nRUN docker-php-ext-install pdo pdo_mysql\n\nRUN pecl install xdebug &amp;&amp; docker-php-ext-enable xdebug\n```\n\n- `xdebug` is installed through `pecl`, which is provided as part of the official PHP image.\n- Rebuild the image with `docker-compose build`,\n-   then restart the server with `docker-compose up`. The output of `phpinfo()` should show that both `pdo_mysql` and `xdebug` are installed.\n\n&gt; vi index.php =&gt; &lt;?php \\r phpinfo();\n\n&gt; docker-compose build # Rebuild the image\n\n&gt; docker-compose up\n&gt; ctrl-c\n\n&gt; vi sqltest.php\n\n```php\n&lt;?php\n$pdo = new PDO('mysql:dbname=tutorialdb;host=mysql', 'tutorial', 'goodsecret', &#91;PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION]);\n\n$query = $pdo-&gt;query('SHOW VARIABLES like \"version\"');\n\n$row = $query-&gt;fetch();\n\necho 'MySQL version:' . $row&#91;'Value'];\n```\n\n&gt; $ docker compose up -d<\/code><\/pre>\n<\/details>\n\n\n\n<h2 class=\"wp-block-heading bottom-border-separator\">Deployment Flows<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On the deployment side, I found I had some decisions to make.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After perusing a dozen or so CI\/CD deployment approaches, and knowing I can&#8217;t learn everything at once, I trimmed the tech list I was looking to learn down to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/fastlane.tools\/\" target=\"_blank\" rel=\"noreferrer noopener\">fastlane<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Actions<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/console.firebase.google.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Firebase<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.jenkins.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jenkins<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The open source app appeared the easiest deployment to tackle first,<\/strong> as it doesn&#8217;t involve publishing to the app stores. And if it were deployed to the web using GitHub Pages, my initial understanding was the entire flow could be handled by GitHub Actions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And while I aim to go through at least 3 or 4 other deployment approaches in the coming weeks to months, <strong>the following pertains to the deployment flow for the open source Flutter project (<code><a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/\" target=\"_blank\" rel=\"noreferrer noopener\">dev-play<\/a><\/code>) using GitHub Actions and GitHub Pages<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading bottom-border-separator\">GitHub Actions =&gt; GH Pages =&gt; Deployment: [web]<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">It all began with an article from <strong>LogRocket<\/strong>: <a href=\"https:\/\/blog.logrocket.com\/flutter-ci-cd-using-github-actions\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter CI\/CD using GitHub Actions<\/a><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Before starting, note that maintaining your workflows and viewing the run history can be done either on <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/blob\/main\/.github\/workflows\/tuple-web-release.yml\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub directly<\/a>, or in your IDE with the help of the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=GitHub.vscode-github-actions\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Actions VS Code extension<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignright size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"612\" height=\"414\" src=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropped.png\" alt=\"GitHub Actions workflow screenshot - cropped more\" class=\"wp-image-413\" style=\"width:340px\" srcset=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropped.png 612w, https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropped-300x203.png 300w\" sizes=\"auto, (max-width: 612px) 100vw, 612px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Setting up GitHub Actions to deploy to GitHub Pages for the open source Flutter App \u2014 even with a step by step walkthrough \u2014 became a tad challenging, and in the end could not be <em>fully<\/em> automated. The one failing automation step can still be performed manually, and is <em>likely<\/em> to do with the action&#8217;s deployment target being a subfolder, and not the root directory (the reason for the subfolder is the open source dev-play repository may end up with more than one app, and so it was set up to target specific <code>release<\/code> branches to trigger individual deployment workflows, and also share the <code>gh-pages<\/code> website structure by each app having its own subfolder).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/blob\/main\/.github\/workflows\/tuple-web-release.yml\" target=\"_blank\" rel=\"noreferrer noopener\">workflow I ended up with<\/a>, pasted below, has a <code>build<\/code> and a <code>deploy<\/code> section. If analyzed for a bit, these action yaml images (script files) are fairly self-explanatory \u2014 that is, once they&#8217;ve been constructed. The details of setting them up is where things can take a bit of time, especially in learning the pros and cons of the various preset actions, depending on your developmental environment needs.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Deployment Automation Workflow: GitHub Actions => GitHub Pages<\/summary>\n<pre class=\"wp-block-code\"><code>name: build_release_tictactuple\n\non:\n  push:\n    branches: &#91; \"build_release_tictactuple\" ]\n  pull_request:\n    branches: &#91; \"build_release_tictactuple\" ]\n\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    defaults:\n      run:\n        working-directory: .\/tic-tac-tuple\n\n    steps:\n      # https:\/\/github.com\/marketplace\/actions\/setup-java-jdk#basic-configuration\n      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it\n      - uses: actions\/checkout@v4\n      # https:\/\/github.com\/Homebrew\/brew\/actions\/runs\/9770285377\/workflow\n      # https:\/\/8thlight.com\/insights\/how-to-set-up-github-actions\n      - name: Set up Homebrew\n        id: set-up-homebrew\n        uses: Homebrew\/actions\/setup-homebrew@master\n      - name: Install YQ and output version\n        id: get_flutter_version\n        # https:\/\/github.com\/subosito\/flutter-action\/issues\/282\n        # https:\/\/github.com\/subosito\/flutter-action\/discussions\/293\n        # https:\/\/github.com\/subosito\/flutter-action\/blob\/b6150a9d644588978ac48783d01e7dbde7031cfa\/README.md\n        run: |\n          brew install yq\n          echo \"result=$(yq '.environment.flutter' pubspec.yaml)\" >> $GITHUB_OUTPUT\n      - name: Set up Java\n        uses: actions\/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version: '21'\n          # cache: 'gradle'\n      - name: Set up Flutter\n        uses: subosito\/flutter-action@v2\n        with:\n          channel: beta\n          # flutter-version: 3.22.0\n          flutter-version: ${{ steps.get_flutter_version.outputs.result }}\n          # flutter-version-file: pubspec.yaml\n          # flutter-version-file: pubspec.yaml | environment: flutter: 3.22.0\n          # flutter-version-file: ${{ steps.get_flutter_version.outputs.result }}\n          cache: true\n\n      # Note: This workflow uses the latest stable version of the Dart SDK.\n      # You can specify other versions if desired, see documentation here:\n      # https:\/\/github.com\/dart-lang\/setup-dart\/blob\/main\/README.md\n      # - uses: dart-lang\/setup-dart@v1\n      # - uses: dart-lang\/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603\n\n      - name: Install dependencies\n        run: flutter pub get\n      - name: Verify formatting\n        run: dart format --output=none --line-length 100 --set-exit-if-changed .\n\n      # Consider passing '--fatal-infos' for slightly stricter analysis.\n      - name: Analyze project source\n        run: dart analyze\n      - name: Run tests\n        run: flutter test\n      - name: Start Web Release Build\n        # run: flutter build web --release\n        run: flutter build web --release --base-href \/dev-play\/tic-tac-tuple\/\n      - name: Upload Web Build Files\n        uses: actions\/upload-artifact@v4\n        with:\n          name: web-release\n          path: .\/tic-tac-tuple\/build\/web\n      - name: List Files\n        run: ls -R\n\n  deploy:\n    name: Deploy Web Build\n    needs: build\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Download Web Release\n      uses: actions\/download-artifact@v4\n      with:\n        name: web-release\n        path: .\/tic-tac-tuple\/build\/web\n\n    # https:\/\/github.com\/peaceiris\/actions-gh-pages\n    - name: Deploy to gh-pages\n      uses: peaceiris\/actions-gh-pages@v4\n      with:\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n        # publish_branch: your-branch  # default: gh-pages\n        destination_dir: tic-tac-tuple\n        publish_dir: .\/tic-tac-tuple\/build\/web\n        # enable_jekyll: true\n        # jekyll_build_options: --trace\n        # keep_files: true<\/code><\/pre>\n<\/details>\n\n\n\n<h4 class=\"wp-block-heading\">Readme.md in GH-Pages Branch<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">You won&#8217;t need this Readme.md if your workflow deployment target is the root directory. But because the dev-play project deploys apps to subfolders, and the action being used (<code>peaceiris<\/code>) doesn&#8217;t auto-trigger the publishing to GitHub Pages, the Readme pasted below helps to work as a manual trigger. It should reside in the root directory of the <code>gh-pages<\/code> branch (a branch that is created automatically on the first workflow run), and so long as specific app deployments are made into subfolders, the Readme file should remain intact. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a deployment were, at some point, to be set up to deploy to the root, then the <code>keep_files<\/code> option could be set to <code>true<\/code> for that workflow, or this Readme could somehow be included in the deployment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As noted in the Readme contents below, although the workflow completes successfully, to complete the actual push to GH Pages, the number (e.g. <code>001<\/code>) at the bottom of the Readme should be increased to allow the branch source change to trigger a push to the GitHub Pages website (after the trigger source is temporarily changed to <code>branch<\/code>).<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Readme.md contents in root of `gh-pages` branch<\/summary>\n<pre class=\"wp-block-code\"><code>For some reason the deployment workflow doesn't trigger GH Pages to publish any updates to the app's website when the `gh-pages` branch is updated via the workflow.\n\nTo get it to publish, when an update is made to the `main` branch, switch to the `build_release_tictactuple` branch, and select the 'update from main' option in GitHub Desktop. Once pushed, it will trigger the 'build and deploy' workflow.\n\n- &#91;View Workflow Runs](https:\/\/github.com\/KDCinfo\/dev-play\/actions\/workflows\/tuple-web-release.yml)\n\nWhen the workflow has completed:\n\n1. Change the &#91;Build and Deployment \"source\"](https:\/\/github.com\/KDCinfo\/dev-play\/settings\/pages) from `GitHub Actions` to `Deploy from a branch`.\n2. Edit and commit this readme to the `gh-pages` branch --- this will trigger the publishing of the site.\n3. Once published, set the trigger source back to `GitHub Actions`.\n\nTimestamp: 2024-07-12 | 001<\/code><\/pre>\n<\/details>\n\n\n\n<h4 class=\"wp-block-heading\">Deploy Actions<\/h4>\n\n\n\n<p class=\"bottom-border-separator has-text-color has-link-color wp-elements-1 wp-block-paragraph\" style=\"color:#000dff\"><code>uses: <strong>peaceiris\/actions-gh-pages@v4<\/strong><\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/peaceiris\/actions-gh-pages\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/peaceiris\/actions-gh-pages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/marketplace\/actions\/github-pages-action\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/marketplace\/actions\/github-pages-action<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A commonly used deployment action is <code>peaceiris\/actions-gh-pages<\/code>. This action works well, but as mentioned just above, had one issue with auto-publishing to GitHub Pages, which is likely to do with the action&#8217;s deployment target being a subfolder, and not the root directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>&#8216;Keep Files&#8217; Option<\/strong>: When you use the <code>peaceiris\/actions-gh-pages<\/code> action, there is a <code><a href=\"https:\/\/github.com\/peaceiris\/actions-gh-pages\/blob\/main\/README.md#%EF%B8%8F-keeping-existing-files-keep_files\" target=\"_blank\" rel=\"noreferrer noopener\">keep_files<\/a><\/code> option.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">By default, existing files in the publish branch (<strong>or only in <code>destination_dir<\/code> if given<\/strong>) will be removed.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Because this workflow uses the <code>destination_dir<\/code> option, files in the project&#8217;s root folder, like the readme, are not removed.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><code>destination_dir: tic-tac-tuple<\/code><br><code># ^--> https:\/\/kdcinfo.github.io\/dev-play\/tic-tac-tuple\/<\/code><br><code># keep_files: true # Not needed; subfolder will be cleaned, but not the root.<\/code><\/p>\n<\/blockquote>\n\n\n\n<p class=\"bottom-border-separator has-text-color has-link-color wp-elements-2 wp-block-paragraph\" style=\"color:#000dff\"><code>uses: <strong>JamesIves\/github-pages-deploy-action@v4<\/strong><\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/JamesIves\/github-pages-deploy-action\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/JamesIves\/github-pages-deploy-action<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/marketplace\/actions\/deploy-to-github-pages\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/marketplace\/actions\/deploy-to-github-pages<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Due to the failure with the automated publishing using the <code>peaceiris<\/code> action, the <code>JamesIves<\/code> action could be tried as an alternative (see @TODO: below). It also has options to target a specific folder and perform cleans.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Web | <code>base-href<\/code><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">As seen in the workflow, <strong>because the app is being deployed into a subfolder<\/strong>, for sites that aren&#8217;t hosted on the web&#8217;s root, you can <strong>use the <code>--base-href<\/code><\/strong> Flutter build option <strong>to point to the app&#8217;s proper subfolder.<\/strong><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><code>run: flutter build web --release <strong>--base-href \/dev-play\/tic-tac-tuple\/<\/strong><\/code><\/p>\n<\/blockquote>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.flutter.dev\/ui\/navigation\/url-strategies#hosting-a-flutter-app-at-a-non-root-location\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/docs.flutter.dev\/ui\/navigation\/url-strategies#hosting-a-flutter-app-at-a-non-root-location<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/64704308\/flutter-web-base-href-subfolder\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/stackoverflow.com\/questions\/64704308\/flutter-web-base-href-subfolder<\/a><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Adding Badges<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Be sure to <a href=\"https:\/\/docs.github.com\/en\/actions\/monitoring-and-troubleshooting-workflows\/adding-a-workflow-status-badge\" target=\"_blank\" rel=\"noreferrer noopener\">add a badge to your Readme.md<\/a> when you&#8217;ve got your workflow running. In addition to being a tad reassuring, it can help visitors to alert you if something out of the ordinary were to happen to your build.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/monitoring-and-troubleshooting-workflows\/adding-a-workflow-status-badge\">https:\/\/docs.github.com\/en\/actions\/monitoring-and-troubleshooting-workflows\/adding-a-workflow-status-badge<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading bottom-border-separator\">Resources<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This project&#8217;s <strong>workflow file<\/strong> (pasted in the expandable section above)<br>&#8211; <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/actions\/workflows\/tuple-web-release.yml\">https:\/\/github.com\/KDCinfo\/dev-play\/actions\/workflows\/tuple-web-release.yml<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Docker Crash Course<\/strong> for Absolute Beginners | DevOps Tools | TechWorld with Nana<br>&#8211; <a href=\"https:\/\/www.youtube.com\/watch?v=pg19Z8LL06w&amp;ab_channel=TechWorldwithNana\">https:\/\/www.youtube.com\/watch?v=pg19Z8LL06w&amp;ab_channel=TechWorldwithNana<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">How to <strong>Get Started with Docker<\/strong> | DockerCon 2020: Best Practices &amp; How To | Docker<br>&#8211; <a href=\"https:\/\/www.youtube.com\/watch?v=iqqDU2crIEQ&amp;t=1029s&amp;ab_channel=Docker\">https:\/\/www.youtube.com\/watch?v=iqqDU2crIEQ&amp;t=1029s&amp;ab_channel=Docker<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading bottom-border-separator\">@TODO:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Change favicon.<\/li>\n\n\n\n<li><s>Add badges (per above).<\/s> <a href=\"https:\/\/github.com\/KDCinfo\/dev-play\/\" target=\"_blank\" rel=\"noreferrer noopener\">Done<\/a><\/li>\n\n\n\n<li>Try alternate deployment action (<code>JamesIves\/github-pages-deploy-action<\/code>).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading bottom-border-separator\">End Result \u2014 Play the Game!<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignright size-medium is-resized\"><a href=\"https:\/\/kdcinfo.github.io\/dev-play\/tic-tac-tuple\/\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"218\" height=\"300\" src=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/tic-tac-tuple_web-play-218x300.png\" alt=\"Tic Tac Tuple - Web Play\" class=\"wp-image-418\" style=\"width:150px\" srcset=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/tic-tac-tuple_web-play-218x300.png 218w, https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/tic-tac-tuple_web-play.png 582w\" sizes=\"auto, (max-width: 218px) 100vw, 218px\" \/><\/a><\/figure>\n<\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>Play <strong>Tic Tac Tuple<\/strong> | <a href=\"https:\/\/kdcinfo.github.io\/dev-play\/tic-tac-tuple\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/kdcinfo.github.io\/dev-play\/tic-tac-tuple\/<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-dots\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Still being new to CI\/CD, does this line up with others&#8217; uses of CI\/CD? I&#8217;m <strong>always open to input.<\/strong> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, I&#8217;ll be moving on to learning <a href=\"https:\/\/fastlane.tools\/\" target=\"_blank\" rel=\"noreferrer noopener\">fastlane<\/a> with <a href=\"https:\/\/play.google.com\/console\/\" target=\"_blank\" rel=\"noreferrer noopener\">Firebase<\/a>, thanks to yet another fabulous walk-through article by <strong>LogRocket<\/strong>: <a href=\"https:\/\/blog.logrocket.com\/fastlane-flutter-complete-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Using fastlane for Flutter: A complete guide<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cheers!<br>&#8211; Keith | <a href=\"https:\/\/keithdc.com\">https:\/\/keithdc.com<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In an effort to become a more well-rounded and full(er) stack developer, the time has come for me to embark on a path to understanding<\/p>\n","protected":false},"author":1,"featured_media":414,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[137,3,138],"tags":[139,142,110,140,141],"class_list":["post-402","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ci-cd","category-flutter","category-github","tag-ci-cd","tag-docker","tag-flutter","tag-github-actions","tag-github-pages"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Preliminary Adventures in CI\/CD with Flutter - App Development: Odds and Ends<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Preliminary Adventures in CI\/CD with Flutter - App Development: Odds and Ends\" \/>\n<meta property=\"og:description\" content=\"In an effort to become a more well-rounded and full(er) stack developer, the time has come for me to embark on a path to understanding\" \/>\n<meta property=\"og:url\" content=\"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/\" \/>\n<meta property=\"og:site_name\" content=\"App Development: Odds and Ends\" \/>\n<meta property=\"article:published_time\" content=\"2024-07-21T10:03:36+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-07-21T10:03:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png\" \/>\n\t<meta property=\"og:image:width\" content=\"967\" \/>\n\t<meta property=\"og:image:height\" content=\"591\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Keith D Commiskey\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Keith D Commiskey\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/\"},\"author\":{\"name\":\"Keith D Commiskey\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#\\\/schema\\\/person\\\/87980b154db7bd0cd7c569ca09328bac\"},\"headline\":\"Preliminary Adventures in CI\\\/CD with Flutter\",\"datePublished\":\"2024-07-21T10:03:36+00:00\",\"dateModified\":\"2024-07-21T10:03:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/\"},\"wordCount\":1340,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#\\\/schema\\\/person\\\/87980b154db7bd0cd7c569ca09328bac\"},\"image\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/dev-play_workflow_tic-tac-tuple_cropless.png\",\"keywords\":[\"CI\\\/CD\",\"Docker\",\"flutter\",\"GitHub Actions\",\"GitHub Pages\"],\"articleSection\":[\"CI\\\/CD\",\"flutter\",\"GitHub\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/\",\"url\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/\",\"name\":\"Preliminary Adventures in CI\\\/CD with Flutter - App Development: Odds and Ends\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/dev-play_workflow_tic-tac-tuple_cropless.png\",\"datePublished\":\"2024-07-21T10:03:36+00:00\",\"dateModified\":\"2024-07-21T10:03:40+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#primaryimage\",\"url\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/dev-play_workflow_tic-tac-tuple_cropless.png\",\"contentUrl\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2024\\\/07\\\/dev-play_workflow_tic-tac-tuple_cropless.png\",\"width\":967,\"height\":591,\"caption\":\"GitHub Actions workflow screenshot - cropped less\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/2024\\\/07\\\/21\\\/preliminary-adventures-in-ci-cd-with-flutter\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Preliminary Adventures in CI\\\/CD with Flutter\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/\",\"name\":\"App Development: Odds and Ends\",\"description\":\"Providing insights into a career of apps and utilities that I&#039;ve conceptualized, designed, and developed; complemented with developmental journeys, findings, observations, and challenges encountered along the way.\",\"publisher\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#\\\/schema\\\/person\\\/87980b154db7bd0cd7c569ca09328bac\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/#\\\/schema\\\/person\\\/87980b154db7bd0cd7c569ca09328bac\",\"name\":\"Keith D Commiskey\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/keith-sac-01.jpg\",\"url\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/keith-sac-01.jpg\",\"contentUrl\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/keith-sac-01.jpg\",\"width\":113,\"height\":117,\"caption\":\"Keith D Commiskey\"},\"logo\":{\"@id\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/keith-sac-01.jpg\"},\"description\":\"Web App Creator; Former Flutter Developer; Former Front End Developer; Former Technical Graphics.\",\"sameAs\":[\"https:\\\/\\\/kdcinfo.com\\\/blog\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/commiskey\"],\"url\":\"https:\\\/\\\/kdcinfo.com\\\/blog\\\/author\\\/kdc4wp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Preliminary Adventures in CI\/CD with Flutter - App Development: Odds and Ends","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/","og_locale":"en_US","og_type":"article","og_title":"Preliminary Adventures in CI\/CD with Flutter - App Development: Odds and Ends","og_description":"In an effort to become a more well-rounded and full(er) stack developer, the time has come for me to embark on a path to understanding","og_url":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/","og_site_name":"App Development: Odds and Ends","article_published_time":"2024-07-21T10:03:36+00:00","article_modified_time":"2024-07-21T10:03:40+00:00","og_image":[{"width":967,"height":591,"url":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png","type":"image\/png"}],"author":"Keith D Commiskey","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Keith D Commiskey","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#article","isPartOf":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/"},"author":{"name":"Keith D Commiskey","@id":"https:\/\/kdcinfo.com\/blog\/#\/schema\/person\/87980b154db7bd0cd7c569ca09328bac"},"headline":"Preliminary Adventures in CI\/CD with Flutter","datePublished":"2024-07-21T10:03:36+00:00","dateModified":"2024-07-21T10:03:40+00:00","mainEntityOfPage":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/"},"wordCount":1340,"commentCount":1,"publisher":{"@id":"https:\/\/kdcinfo.com\/blog\/#\/schema\/person\/87980b154db7bd0cd7c569ca09328bac"},"image":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#primaryimage"},"thumbnailUrl":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png","keywords":["CI\/CD","Docker","flutter","GitHub Actions","GitHub Pages"],"articleSection":["CI\/CD","flutter","GitHub"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/","url":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/","name":"Preliminary Adventures in CI\/CD with Flutter - App Development: Odds and Ends","isPartOf":{"@id":"https:\/\/kdcinfo.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#primaryimage"},"image":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#primaryimage"},"thumbnailUrl":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png","datePublished":"2024-07-21T10:03:36+00:00","dateModified":"2024-07-21T10:03:40+00:00","breadcrumb":{"@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#primaryimage","url":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png","contentUrl":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2024\/07\/dev-play_workflow_tic-tac-tuple_cropless.png","width":967,"height":591,"caption":"GitHub Actions workflow screenshot - cropped less"},{"@type":"BreadcrumbList","@id":"https:\/\/kdcinfo.com\/blog\/2024\/07\/21\/preliminary-adventures-in-ci-cd-with-flutter\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/kdcinfo.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Preliminary Adventures in CI\/CD with Flutter"}]},{"@type":"WebSite","@id":"https:\/\/kdcinfo.com\/blog\/#website","url":"https:\/\/kdcinfo.com\/blog\/","name":"App Development: Odds and Ends","description":"Providing insights into a career of apps and utilities that I&#039;ve conceptualized, designed, and developed; complemented with developmental journeys, findings, observations, and challenges encountered along the way.","publisher":{"@id":"https:\/\/kdcinfo.com\/blog\/#\/schema\/person\/87980b154db7bd0cd7c569ca09328bac"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/kdcinfo.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/kdcinfo.com\/blog\/#\/schema\/person\/87980b154db7bd0cd7c569ca09328bac","name":"Keith D Commiskey","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2023\/08\/keith-sac-01.jpg","url":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2023\/08\/keith-sac-01.jpg","contentUrl":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2023\/08\/keith-sac-01.jpg","width":113,"height":117,"caption":"Keith D Commiskey"},"logo":{"@id":"https:\/\/kdcinfo.com\/blog\/wp-content\/uploads\/2023\/08\/keith-sac-01.jpg"},"description":"Web App Creator; Former Flutter Developer; Former Front End Developer; Former Technical Graphics.","sameAs":["https:\/\/kdcinfo.com\/blog","https:\/\/www.linkedin.com\/in\/commiskey"],"url":"https:\/\/kdcinfo.com\/blog\/author\/kdc4wp\/"}]}},"_links":{"self":[{"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/posts\/402","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/comments?post=402"}],"version-history":[{"count":9,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/posts\/402\/revisions"}],"predecessor-version":[{"id":419,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/posts\/402\/revisions\/419"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/media\/414"}],"wp:attachment":[{"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/media?parent=402"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/categories?post=402"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kdcinfo.com\/blog\/wp-json\/wp\/v2\/tags?post=402"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}