├── tools ├── setup.sh ├── git-import-patches ├── git-export-patches └── lib │ ├── patches.py │ └── git.py ├── .gitignore ├── flock └── .gclient ├── .github ├── ISSUE_TEMPLATE │ ├── nest---a-bug-in-forking-tools.md │ ├── nest---a-forking-tools-feature.md │ ├── flock---a-feature-that-flutter-won-t-implement.md │ └── flock---a-bug-that-flutter-won-t-fix.md ├── actions │ ├── push-flock-repo │ │ └── action.yaml │ ├── run-framework-tests │ │ └── action.yaml │ ├── build-and-upload-engine │ │ └── action.yaml │ └── assemble-flock │ │ └── action.yaml └── workflows │ ├── daily-flock-rebase.yaml │ └── build-and-upload-engine-variants.yaml └── README.md /tools/setup.sh: -------------------------------------------------------------------------------- 1 | cd flock 2 | gclient sync 3 | git checkout master 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac files. 2 | .DS_Store 3 | 4 | # python cache 5 | tools/lib/__pycache__/ 6 | 7 | # Ignore flock so we don't accidently commit it 8 | flock 9 | !flock/.gclient 10 | -------------------------------------------------------------------------------- /flock/.gclient: -------------------------------------------------------------------------------- 1 | solutions = [ 2 | { 3 | "custom_deps": {}, 4 | "deps_file": "DEPS", 5 | "managed": False, 6 | "name": ".", 7 | "safesync_url": "", 8 | "url": "https://github.com/flutter/flutter.git", 9 | }, 10 | ] 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/nest---a-bug-in-forking-tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Nest - A bug in forking tools 3 | about: Report a bug in the Nest forking tools 4 | title: "[Nest][Bug] - A good bug title here" 5 | labels: bug, nest 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/actions/push-flock-repo/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Update Remote Flock Branch' 2 | description: 'Pushes the Flock repo (will force push and replace whatever is in the destination branch)' 3 | inputs: 4 | destination_repo: 5 | required: true 6 | description: "the repo to push the flock build to" 7 | 8 | destination_branch: 9 | required: true 10 | description: "the branch to push the flock build to" 11 | 12 | runs: 13 | using: "composite" 14 | steps: 15 | - name: Push to destination 16 | shell: bash 17 | run: | 18 | cd flock 19 | git remote add destination https://github.com/${{ inputs.destination_repo }} 20 | git checkout -b flock-temp 21 | git push -f destination flock-temp:${{ inputs.destination_branch}} 22 | -------------------------------------------------------------------------------- /.github/actions/run-framework-tests/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Run Framework Tests' 2 | description: 'Runs the Flutter Framework Tests' 3 | inputs: 4 | flutter_path: 5 | required: true 6 | description: "the absolute path to flock" 7 | 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: Setup Python 12 | uses: actions/setup-python@v5 13 | with: 14 | python-version: '3.12' 15 | 16 | - name: Setup Android Tools 17 | shell: bash 18 | run: | 19 | sudo apt-get install android-tools-adb 20 | 21 | - name: Run Tests 22 | shell: bash 23 | run: | 24 | PATH="$PATH:`pwd`/${{inputs.flutter_path}}/bin" 25 | flutter update-packages 26 | cd ${{inputs.flutter_path}}/packages/flutter 27 | flutter test 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/nest---a-forking-tools-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Nest - A forking tools feature 3 | about: Suggest a feature for the tools we use to fork Flutter 4 | title: "[Nest][Feature] - A good feature title here" 5 | labels: feature, nest 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nest 2 | Tools for hatching new Flutters. 3 | 4 | Learn more at https://getflocked.dev. 5 | 6 | ## Filing Issues 7 | This repository is used to file issues for both Flock and Nest. 8 | 9 | If you're having any issues with Flock or Nest, or if you have a feature request, please file a ticket under "Issues". 10 | 11 | ## What is Nest? 12 | Nest is a collection of tools that make it easier to maintain a fork of Flutter. 13 | 14 | There are various reasons that one might fork Flutter: 15 | 16 | * To fix a Flutter bug locally, while you wait for the Flutter team to fix it centrally. 17 | * To add needed features to Flutter that the Flutter team has chosen not to implement. 18 | * To port Flutter to a custom embedded system. 19 | 20 | Forking Flutter requires constant maintenance: 21 | 22 | * Constantly synchronize with upstream Flutter changes, to stay up to date. 23 | * Build and deliver custom engine builds. 24 | 25 | Nest aims to create and collect various scripts and apps that make it easier to maintain 26 | and distribute Flutter forks. 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/flock---a-feature-that-flutter-won-t-implement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Flock - A feature that Flutter won't implement 3 | about: Suggest a feature that you think should be in the Flutter framework 4 | title: "[Flock][Feature] - A good feature title here" 5 | labels: feature, flock 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Flutter Issue Ticket Link:** 11 | Insert a link to the existing Flutter issue ticket. You should only file a feature request with Flock if you've already tried to get the Flutter team to implement it, first. 12 | 13 | **Why didn't Flutter implement this?** 14 | Explain why Flutter didn't or won't implement this. 15 | 16 | **Why does this belong in Flock?** 17 | Provide a clear rationale for why this belongs in Flock, instead of in a package. 18 | 19 | ------ 20 | For each of the following sections, check if the existing Flutter ticket has this information. If not, add that information here. If it does, delete the following sections. Also, delete this note. 21 | ------ 22 | 23 | **Describe the solution you'd like** 24 | A clear and concise description of what you want to happen. 25 | 26 | **Additional context** 27 | Add any other context or screenshots about the feature request here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/flock---a-bug-that-flutter-won-t-fix.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Flock - A bug that Flutter won't fix 3 | about: Report a bug that Flutter can't or won't fix 4 | title: "[Flock][Bug] - Replace this with a good title" 5 | labels: bug, flock 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Flutter Issue Ticket Link:** 11 | Insert a link to the existing Flutter issue ticket. You should only file a bug with Flock if you've already tried to get the Flutter team to fix it. 12 | 13 | ------ 14 | For each of the following sections, check if the existing Flutter ticket has this information. If not, add that information here. If it does, delete the following sections. Also, delete this note. 15 | ------ 16 | 17 | **Additional details** 18 | Add general details that aren't available in the Flutter ticket. 19 | 20 | **To Reproduce** 21 | If the original Flutter ticket doesn't contain reproduction steps, add them here, or delete this section. 22 | 1. Go to '...' 23 | 2. Click on '....' 24 | 3. Scroll down to '....' 25 | 4. See error 26 | 27 | **Expected behavior** 28 | A clear and concise description of what you expected to happen. 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | **Impacted platforms** 34 | Android, iOS, Web, Mac, Windows, Linux 35 | 36 | **Flutter version** 37 | Add your `flutter --version` output here. 38 | -------------------------------------------------------------------------------- /tools/git-import-patches: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) Electron contributors 4 | # Copyright (c) 2013-2020 GitHub Inc. 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining 7 | # a copy of this software and associated documentation files (the 8 | # "Software"), to deal in the Software without restriction, including 9 | # without limitation the rights to use, copy, modify, merge, publish, 10 | # distribute, sublicense, and/or sell copies of the Software, and to 11 | # permit persons to whom the Software is furnished to do so, subject to 12 | # the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | import argparse 26 | import sys 27 | 28 | from lib import git 29 | from lib.patches import patch_from_dir 30 | from pathlib import Path 31 | 32 | 33 | def main(argv): 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument("-f", "--flutter", 36 | help="directory containing a flutter repo to patch", 37 | default="./flock") 38 | parser.add_argument("-p", "--patches-dir", 39 | help="directory containing patches to apply") 40 | parser.add_argument("-3", "--3way", 41 | action="store_true", dest='threeway', 42 | help="use 3-way merge to resolve conflicts") 43 | args = parser.parse_args(argv) 44 | 45 | git.import_patches( 46 | repo=Path(args.flutter).expanduser().resolve(strict=False), 47 | patch_data=patch_from_dir(Path(args.patches_dir).expanduser().resolve(strict=False)), 48 | 49 | threeway=args.threeway 50 | ) 51 | 52 | 53 | if __name__ == '__main__': 54 | main(sys.argv[1:]) 55 | -------------------------------------------------------------------------------- /.github/workflows/daily-flock-rebase.yaml: -------------------------------------------------------------------------------- 1 | # This workflow runs once per day and recreates the Flock master, beta, and 2 | # stable branches with the latest history from Flutter. 3 | # 4 | # The Flock branches are reconstituted by taking the most recent history of 5 | # Flutter's branches, and then replaying all of Flock's changes on top. 6 | # This replay is effectively a rebase, but each change is saved in a patch 7 | # file instead of the git log. 8 | 9 | on: 10 | schedule: 11 | - cron: '0 12 * * *' # Once per day at noon 12 | workflow_dispatch: # on button click 13 | 14 | # Update these variables to fit your custom fork configuration. 15 | env: 16 | # Expected GitHub repository variables: 17 | # 18 | # - "FLOCK_REPO": The GitHub location of your Flutter fork. 19 | # WARNING: THIS WORKFLOW WILL DESTROY HISTORY IN THIS REPO!! 20 | FLOCK_REPO: ${{ vars.FLOCK_REPO }} 21 | 22 | # A Personal Access Token (PAT) so that this action can destroy and create 23 | # history in the `FLOCK_REPO`. 24 | GITHUB_PAT: ${{ secrets.REPO_WORKFLOW_PAT }} 25 | 26 | # A repo to use for patches 27 | PATCHES_REPO: ${{ vars.FLOCK_PATCHES_REPO }} 28 | 29 | name: daily-flock-rebase 30 | permissions: 31 | contents: write 32 | 33 | jobs: 34 | # This job runs for "master", "beta", and "stable" Flock branches. 35 | rebase-flock-on-flutter: 36 | runs-on: ubuntu-latest 37 | 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | config: [master, beta, stable] 42 | 43 | steps: 44 | - name: Check out the Nest Repository 45 | uses: actions/checkout@v4 46 | 47 | - name: Assemble Flock 48 | uses: ./.github/actions/assemble-flock 49 | with: 50 | flutter_ref: ${{ matrix.config }} 51 | token: ${{ secrets.REPO_WORKFLOW_PAT }} 52 | patches_repo: ${{ env.PATCHES_REPO }} 53 | patches_ref: ${{ matrix.config }} 54 | 55 | 56 | - name: Update Remote Flock Branch, will force push our assembled version (master, beta, stable) 57 | uses: ./.github/actions/push-flock-repo 58 | with: 59 | destination_repo: ${{ env.FLOCK_REPO }} 60 | destination_branch: ${{ matrix.config }} 61 | -------------------------------------------------------------------------------- /tools/git-export-patches: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) Electron contributors 4 | # Copyright (c) 2013-2020 GitHub Inc. 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining 7 | # a copy of this software and associated documentation files (the 8 | # "Software"), to deal in the Software without restriction, including 9 | # without limitation the rights to use, copy, modify, merge, publish, 10 | # distribute, sublicense, and/or sell copies of the Software, and to 11 | # permit persons to whom the Software is furnished to do so, subject to 12 | # the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | import argparse 26 | import sys 27 | 28 | from lib import git 29 | from pathlib import Path 30 | 31 | def main(argv): 32 | parser = argparse.ArgumentParser() 33 | parser.add_argument("-f", "--flutter", 34 | help="directory containing a flutter repo to patch", 35 | default="./flock") 36 | parser.add_argument("-p", "--patches-dir", 37 | help="directory into which exported patches will be written", 38 | required=True) 39 | parser.add_argument("--grep", 40 | help="only export patches matching a keyword") 41 | parser.add_argument("patch_range", 42 | nargs='?', 43 | help="range of patches to export. Defaults to all commits since the " 44 | "most recent tag or remote branch.") 45 | args = parser.parse_args(argv) 46 | 47 | git.export_patches(Path(args.flutter).expanduser().resolve(strict=False), Path(args.patches_dir).expanduser().resolve(strict=False), patch_range=args.patch_range, grep=args.grep) 48 | 49 | 50 | if __name__ == '__main__': 51 | main(sys.argv[1:]) 52 | -------------------------------------------------------------------------------- /tools/lib/patches.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) Electron contributors 4 | # Copyright (c) 2013-2020 GitHub Inc. 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining 7 | # a copy of this software and associated documentation files (the 8 | # "Software"), to deal in the Software without restriction, including 9 | # without limitation the rights to use, copy, modify, merge, publish, 10 | # distribute, sublicense, and/or sell copies of the Software, and to 11 | # permit persons to whom the Software is furnished to do so, subject to 12 | # the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | import codecs 26 | import os 27 | 28 | PATCH_DIR_PREFIX = "Patch-Dir: " 29 | PATCH_FILENAME_PREFIX = "Patch-Filename: " 30 | PATCH_LINE_PREFIXES = (PATCH_DIR_PREFIX, PATCH_FILENAME_PREFIX) 31 | 32 | 33 | def is_patch_location_line(line): 34 | return line.startswith(PATCH_LINE_PREFIXES) 35 | 36 | def read_patch(patch_dir, patch_filename): 37 | """Read a patch from |patch_dir/filename| and amend the commit message with 38 | metadata about the patch file it came from.""" 39 | ret = [] 40 | added_patch_location = False 41 | patch_path = os.path.join(patch_dir, patch_filename) 42 | with codecs.open(patch_path, encoding='utf-8') as f: 43 | for l in f.readlines(): 44 | line_has_correct_start = l.startswith('diff -') or l.startswith('---') 45 | if not added_patch_location and line_has_correct_start: 46 | ret.append(f'{PATCH_DIR_PREFIX}{patch_dir}\n') 47 | ret.append(f'{PATCH_FILENAME_PREFIX}{patch_filename}\n') 48 | added_patch_location = True 49 | ret.append(l) 50 | return ''.join(ret) 51 | 52 | 53 | def patch_from_dir(patch_dir): 54 | """Read a directory of patches into a format suitable for passing to 55 | 'git am'""" 56 | with open(os.path.join(patch_dir, ".patches"), encoding='utf-8') as file_in: 57 | patch_list = [line.rstrip('\n') for line in file_in.readlines()] 58 | 59 | return ''.join([ 60 | read_patch(patch_dir, patch_filename) 61 | for patch_filename in patch_list 62 | ]) 63 | -------------------------------------------------------------------------------- /.github/actions/build-and-upload-engine/action.yaml: -------------------------------------------------------------------------------- 1 | # .github/actions/build-and-upload-engine/action.yml 2 | name: "Build and Upload Engine" 3 | description: "Build the engine with a specific gclient setup and config name" 4 | inputs: 5 | 6 | gclient: 7 | description: "name of gclient configuration to use, nest includes a few custom gclient files that reduce the size of the engine build so that it fits on a standard github runner" 8 | required: true 9 | 10 | config_name: 11 | description: "name of a Flutter Engine Tool (et) config (as defined in one of the builder configs in engine/src/flutter/ci/builders)" 12 | required: true 13 | 14 | engine_version: 15 | description: "the engine hash that the output assets will correspond to (should match the engine version hash computed when assembling Flock)" 16 | required: true 17 | 18 | gcp_credentials: 19 | description: "the credentials for a GCP storage bucket that will store the engine build binaries" 20 | required: false 21 | default: '' 22 | 23 | gcp_bucket: 24 | description: "the bucket to upload the binaries from the engine build so that they can be downloaded by the flutter tool" 25 | required: false 26 | default: '' 27 | 28 | runs: 29 | using: "composite" 30 | steps: 31 | - name: 'Set up Cloud SDK' 32 | uses: 'google-github-actions/setup-gcloud@v2' 33 | with: 34 | version: '>= 363.0.0' 35 | 36 | - name: Setup gcloud 37 | if: ${{ inputs.gcp_bucket != '' && inputs.gcp_credentials != '' }} 38 | shell: bash 39 | env: 40 | GCP_CREDENTIALS: ${{ inputs.gcp_credentials }} 41 | run: | 42 | echo "$GCP_CREDENTIALS" | base64 -d > "$HOME"/gcloud.json 43 | gcloud auth activate-service-account --key-file="$HOME"/gcloud.json 44 | 45 | - name: Free disk space 46 | shell: bash 47 | if: contains(matrix.os, 'ubuntu') 48 | run: curl -fsSL https://raw.githubusercontent.com/kou/arrow/e49d8ae15583ceff03237571569099a6ad62be32/ci/scripts/util_free_space.sh | bash 49 | 50 | - uses: actions/setup-python@v5 51 | with: 52 | python-version: '3.13' 53 | 54 | - name: 55 | if: ${{ contains(inputs.config_name, 'android') == true }} 56 | shell: bash 57 | run: | 58 | cd flock 59 | engine/src/build/linux/sysroot_scripts/install-sysroot.py --arch=x64 60 | 61 | - name: Set up depot_tools / gclient 62 | shell: bash 63 | run: | 64 | git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $HOME/depot_tools 65 | # Append depot_tools to the PATH for subsequent steps 66 | echo "$HOME/depot_tools" >> $GITHUB_PATH 67 | 68 | - name: Copy gclient file 69 | shell: bash 70 | run: | 71 | # Use custom gclient to trim size 72 | cd flock 73 | cp engine/scripts/${{ inputs.gclient }}.gclient .gclient 74 | 75 | - name: Gclient sync 76 | shell: bash 77 | run: | 78 | cd flock 79 | gclient sync -D 80 | 81 | - name: Fetch engine dependencies 82 | shell: bash 83 | run: | 84 | cd flock/engine/src 85 | ./flutter/bin/et fetch 86 | 87 | - name: Build Engine 88 | shell: bash 89 | run: | 90 | echo "Running et config: ${{ inputs.config_name }}" 91 | cd flock/engine/src 92 | ./flutter/bin/et build --config ${{ inputs.config_name }} 93 | 94 | - name: Copy Zip Archives 95 | shell: bash 96 | if: ${{ hashFiles(format('flock/engine/src/out/{0}/zip_archives/**', inputs.config_name))!= '' && contains(inputs.config_name, 'test') == false && inputs.gcp_bucket != ''}} 97 | run: | 98 | cd flock/engine/src 99 | echo "copying output to /build_outputs/flutter/engine///" 100 | gcloud storage cp --recursive out/${{ inputs.config_name }}/zip_archives/* gs://${{inputs.gcp_bucket}}/flutter_infra_release/flutter/engine/${{ inputs.engine_version }}/ 101 | 102 | - name: Copy Engine Outputs 103 | shell: bash 104 | if: ${{ hashFiles(format('flock/engine/src/out/{0}/zip_archives/**', inputs.config_name)) == '' && contains(inputs.config_name, 'test') == false && inputs.gcp_bucket != '' }} 105 | run: | 106 | cd flock/engine/src 107 | echo "copying output to /build_outputs/flutter/engine///" 108 | tar c out/${{ inputs.config_name }} | gzip > out/${{ inputs.config_name }}.tar.gz 109 | gcloud storage cp --recursive out/${{ inputs.config_name }}.tar.gz gs://${{inputs.gcp_bucket}}/flutter_infra_release/flutter/engine/${{ inputs.engine_version }}/${{ inputs.config_name }}.tar.gz 110 | -------------------------------------------------------------------------------- /.github/actions/assemble-flock/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Assemble Flock' 2 | description: 'Checkout flock into the path "flock" and then apply the patches' 3 | inputs: 4 | flutter_ref: 5 | required: true 6 | description: "the ref of the flutter repo to use as a base (typically the most recent commit on either master, stable, or beta)" 7 | 8 | flutter_repo: 9 | required: false 10 | description: "the flutter repo to use as a base" 11 | default: flutter/flutter 12 | 13 | token: 14 | required: false 15 | description: "An access token, such as a personal access token (PAT) or organization token, which has read access to the base repo (usually flutter/flutter), and also has write access to the fork repo (e.g., join-the-flock/flock). Even though this step doesn't push any changes to the fork repo, GitHub won't allow any future push to the fork repo, unless the base repo is originally checked out with that token. Therefore, the token must be used up front at this stage during initial checkout and assembly." 16 | default: ${{ github.token }} 17 | 18 | fetch_depth: 19 | description: "The git fetch depth used when checking out the base repo (typically Flutter). This action walks the engine history to select an engine hash. When the fetch depth is zero, that history walking should always work. If the fetch depth is 1+, it's possible that an unexpected engine hash is selected. Use at own discretion." 20 | required: false 21 | default: 0 22 | 23 | patches_ref: 24 | required: true 25 | description: "the ref of the branch or ref to use for patching flutter" 26 | default: main 27 | 28 | patches_repo: 29 | required: false 30 | description: "the repo to use for patches" 31 | default: join-the-flock/flock-patches 32 | 33 | runs: 34 | using: "composite" 35 | steps: 36 | - name: checkout base flutter ref 37 | uses: actions/checkout@v4 38 | with: 39 | path: 'flock' 40 | fetch-depth: ${{inputs.fetch_depth}} 41 | repository: ${{inputs.flutter_repo}} 42 | ref: '${{inputs.flutter_ref}}' 43 | token: ${{ inputs.token }} 44 | 45 | - name: checkout patches ref 46 | uses: actions/checkout@v4 47 | with: 48 | path: 'patches' 49 | repository: ${{inputs.patches_repo}} 50 | ref: '${{inputs.patches_ref}}' 51 | token: ${{ inputs.token }} 52 | 53 | # Compute an engine version hash so the flutter tool will be able to find a precompiled engine 54 | - name: save engine hash 55 | shell: bash 56 | run: | 57 | cd ./flock 58 | ./bin/internal/update_engine_version.sh 59 | cp ./bin/cache/engine.stamp ./bin/internal/engine.version 60 | 61 | git add -f ./bin/internal/engine.version 62 | git config --global user.email "join-the-flock@users.noreply.github.com" 63 | git config --global user.name "join-the-flock" 64 | git commit -m "save engine version" --allow-empty 65 | 66 | - name: Apply Flock patches on top of Flutter 67 | shell: bash 68 | run: | 69 | set -euo pipefail 70 | 71 | if ! tools/git-import-patches -p patches -3 2> patch_fail.log; then 72 | echo "::error::git-import-patches failed. Showing conflicts and context…" 73 | 74 | echo "::group::Last 200 lines of git-import-patches stderr" 75 | tail -n 200 patch_fail.log || true 76 | echo "::endgroup::" 77 | 78 | echo "::group::Git status (flock)" 79 | (cd flock && git status --porcelain=v1 && echo && git diff --name-only --diff-filter=U || true) 80 | echo "::endgroup::" 81 | 82 | echo "::group::Conflicts with full context (flock)" 83 | ( 84 | cd flock || exit 0 85 | files="$(git diff --name-only --diff-filter=U || true)" 86 | if [ -n "$files" ]; then 87 | for f in $files; do 88 | echo "--- $f ---" 89 | # Print the full conflict blocks with content 90 | awk ' 91 | BEGIN { inconflict=0 } 92 | /^<<<<<<< / { inconflict=1; print "----- conflict start (" FILENAME ":" NR ") -----"; print; next } 93 | /^>>>>>>> / { print; print "----- conflict end (" FILENAME ":" NR ") -----"; inconflict=0; next } 94 | inconflict { print } 95 | ' "$f" || true 96 | done 97 | else 98 | # Fallback: grep recursively and show context if git didn't detect 99 | grep -R -n -C3 -E "^(<<<<<<<|=======|>>>>>>>)" . || true 100 | fi 101 | ) 102 | echo "::endgroup::" 103 | 104 | exit 1 105 | fi 106 | 107 | -------------------------------------------------------------------------------- /tools/lib/git.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) Electron contributors 4 | # Copyright (c) 2013-2020 GitHub Inc. 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining 7 | # a copy of this software and associated documentation files (the 8 | # "Software"), to deal in the Software without restriction, including 9 | # without limitation the rights to use, copy, modify, merge, publish, 10 | # distribute, sublicense, and/or sell copies of the Software, and to 11 | # permit persons to whom the Software is furnished to do so, subject to 12 | # the following conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | """Git helper functions. 26 | 27 | Everything here should be project agnostic: it shouldn't rely on project's 28 | structure, or make assumptions about the passed arguments or calls' outcomes. 29 | """ 30 | 31 | import io 32 | import os 33 | import posixpath 34 | import re 35 | import subprocess 36 | import sys 37 | 38 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 39 | sys.path.append(SCRIPT_DIR) 40 | 41 | from patches import PATCH_FILENAME_PREFIX, is_patch_location_line 42 | 43 | UPSTREAM_HEAD='refs/patches/upstream-head' 44 | 45 | def is_repo_root(path): 46 | path_exists = os.path.exists(path) 47 | if not path_exists: 48 | return False 49 | 50 | git_folder_path = os.path.join(path, '.git') 51 | git_folder_exists = os.path.exists(git_folder_path) 52 | 53 | return git_folder_exists 54 | 55 | 56 | def get_repo_root(path): 57 | """Finds a closest ancestor folder which is a repo root.""" 58 | norm_path = os.path.normpath(path) 59 | norm_path_exists = os.path.exists(norm_path) 60 | if not norm_path_exists: 61 | return None 62 | 63 | if is_repo_root(norm_path): 64 | return norm_path 65 | 66 | parent_path = os.path.dirname(norm_path) 67 | 68 | # Check if we're in the root folder already. 69 | if parent_path == norm_path: 70 | return None 71 | 72 | return get_repo_root(parent_path) 73 | 74 | 75 | def am(repo, patch_data, threeway=False, directory=None, exclude=None, 76 | committer_name=None, committer_email=None, keep_cr=True): 77 | args = [] 78 | if threeway: 79 | args += ['--3way'] 80 | if directory is not None: 81 | args += ['--directory', directory] 82 | if exclude is not None: 83 | for path_pattern in exclude: 84 | args += ['--exclude', path_pattern] 85 | if keep_cr is True: 86 | # Keep the CR of CRLF in case any patches target files with Windows line 87 | # endings. 88 | args += ['--keep-cr'] 89 | 90 | root_args = ['-C', repo] 91 | if committer_name is not None: 92 | root_args += ['-c', 'user.name=' + committer_name] 93 | if committer_email is not None: 94 | root_args += ['-c', 'user.email=' + committer_email] 95 | root_args += ['-c', 'commit.gpgsign=false'] 96 | command = ['git'] + root_args + ['am'] + args 97 | with subprocess.Popen(command, stdin=subprocess.PIPE) as proc: 98 | proc.communicate(patch_data.encode('utf-8')) 99 | if proc.returncode != 0: 100 | raise RuntimeError(f"Command {command} returned {proc.returncode}") 101 | 102 | 103 | def import_patches(repo, ref=UPSTREAM_HEAD, **kwargs): 104 | """same as am(), but we save the upstream HEAD so we can refer to it when we 105 | later export patches""" 106 | update_ref(repo=repo, ref=ref, newvalue='HEAD') 107 | am(repo=repo, **kwargs) 108 | 109 | 110 | def update_ref(repo, ref, newvalue): 111 | args = ['git', '-C', repo, 'update-ref', ref, newvalue] 112 | 113 | return subprocess.check_call(args) 114 | 115 | 116 | def get_commit_for_ref(repo, ref): 117 | args = ['git', '-C', repo, 'rev-parse', '--verify', ref] 118 | return subprocess.check_output(args).decode('utf-8').strip() 119 | 120 | def get_commit_count(repo, commit_range): 121 | args = ['git', '-C', repo, 'rev-list', '--count', commit_range] 122 | return int(subprocess.check_output(args).decode('utf-8').strip()) 123 | 124 | def guess_base_commit(repo, ref): 125 | """Guess which commit the patches might be based on""" 126 | try: 127 | upstream_head = get_commit_for_ref(repo, ref) 128 | num_commits = get_commit_count(repo, upstream_head + '..') 129 | return [upstream_head, num_commits] 130 | except subprocess.CalledProcessError: 131 | args = [ 132 | 'git', 133 | '-C', 134 | repo, 135 | 'describe', 136 | '--tags', 137 | ] 138 | return subprocess.check_output(args).decode('utf-8').rsplit('-', 2)[0:2] 139 | 140 | 141 | def format_patch(repo, since): 142 | args = [ 143 | 'git', 144 | '-C', 145 | repo, 146 | '-c', 147 | 'core.attributesfile=' 148 | + os.path.join( 149 | os.path.dirname(os.path.realpath(__file__)), 150 | 'electron.gitattributes', 151 | ), 152 | # Ensure it is not possible to match anything 153 | # Disabled for now as we have consistent chunk headers 154 | # '-c', 155 | # 'diff.electron.xfuncname=$^', 156 | 'format-patch', 157 | '--keep-subject', 158 | '--no-stat', 159 | '--stdout', 160 | 161 | # Per RFC 3676 the signature is separated from the body by a line with 162 | # '-- ' on it. If the signature option is omitted the signature defaults 163 | # to the Git version number. 164 | '--no-signature', 165 | 166 | # The name of the parent commit object isn't useful information in this 167 | # context, so zero it out to avoid needless patch-file churn. 168 | '--zero-commit', 169 | 170 | # Some versions of git print out different numbers of characters in the 171 | # 'index' line of patches, so pass --full-index to get consistent 172 | # behaviour. 173 | '--full-index', 174 | since 175 | ] 176 | return subprocess.check_output(args).decode('utf-8') 177 | 178 | 179 | def split_patches(patch_data): 180 | """Split a concatenated series of patches into N separate patches""" 181 | patches = [] 182 | patch_start = re.compile('^From [0-9a-f]+ ') 183 | # Keep line endings in case any patches target files with CRLF. 184 | keep_line_endings = True 185 | for line in patch_data.splitlines(keep_line_endings): 186 | if patch_start.match(line): 187 | patches.append([]) 188 | patches[-1].append(line) 189 | return patches 190 | 191 | def filter_patches(patches, key): 192 | """Return patches that include the specified key""" 193 | if key is None: 194 | return patches 195 | matches = [] 196 | for patch in patches: 197 | if any(key in line for line in patch): 198 | matches.append(patch) 199 | continue 200 | return matches 201 | 202 | def munge_subject_to_filename(subject): 203 | """Derive a suitable filename from a commit's subject""" 204 | if subject.endswith('.patch'): 205 | subject = subject[:-6] 206 | return re.sub(r'[^A-Za-z0-9-]+', '_', subject).strip('_').lower() + '.patch' 207 | 208 | 209 | def get_file_name(patch): 210 | """Return the name of the file to which the patch should be written""" 211 | file_name = None 212 | for line in patch: 213 | if line.startswith(PATCH_FILENAME_PREFIX): 214 | file_name = line[len(PATCH_FILENAME_PREFIX):] 215 | break 216 | # If no patch-filename header, munge the subject. 217 | if not file_name: 218 | for line in patch: 219 | if line.startswith('Subject: '): 220 | file_name = munge_subject_to_filename(line[len('Subject: '):]) 221 | break 222 | return file_name.rstrip('\n') 223 | 224 | 225 | def join_patch(patch): 226 | """Joins and formats patch contents""" 227 | return ''.join(remove_patch_location(patch)).rstrip('\n') + '\n' 228 | 229 | 230 | def remove_patch_location(patch): 231 | """Strip out the patch location lines from a patch's message body""" 232 | force_keep_next_line = False 233 | n = len(patch) 234 | for i, l in enumerate(patch): 235 | skip_line = is_patch_location_line(l) 236 | skip_next = i < n - 1 and is_patch_location_line(patch[i + 1]) 237 | if not force_keep_next_line and ( 238 | skip_line or (skip_next and len(l.rstrip()) == 0) 239 | ): 240 | pass # drop this line 241 | else: 242 | yield l 243 | force_keep_next_line = l.startswith('Subject: ') 244 | 245 | 246 | def export_patches(repo, out_dir, 247 | patch_range=None, ref=UPSTREAM_HEAD, 248 | dry_run=False, grep=None): 249 | if not os.path.exists(repo): 250 | sys.stderr.write( 251 | f"Skipping patches in {repo} because it does not exist.\n" 252 | ) 253 | return 254 | if patch_range is None: 255 | patch_range, n_patches = guess_base_commit(repo, ref) 256 | msg = f"Exporting {n_patches} patches in {repo} since {patch_range[0:7]}\n" 257 | sys.stderr.write(msg) 258 | patch_data = format_patch(repo, patch_range) 259 | patches = split_patches(patch_data) 260 | if grep: 261 | olen = len(patches) 262 | patches = filter_patches(patches, grep) 263 | sys.stderr.write(f"Exporting {len(patches)} of {olen} patches\n") 264 | 265 | try: 266 | os.mkdir(out_dir) 267 | except OSError: 268 | pass 269 | 270 | if dry_run: 271 | # If we're doing a dry run, iterate through each patch and see if the newly 272 | # exported patch differs from what exists. Report number of mismatched 273 | # patches and fail if there's more than one. 274 | bad_patches = [] 275 | for patch in patches: 276 | filename = get_file_name(patch) 277 | filepath = posixpath.join(out_dir, filename) 278 | with io.open(filepath, 'rb') as inp: 279 | existing_patch = str(inp.read(), 'utf-8') 280 | formatted_patch = join_patch(patch) 281 | if formatted_patch != existing_patch: 282 | bad_patches.append(filename) 283 | if len(bad_patches) > 0: 284 | sys.stderr.write( 285 | "Patches in {} not up to date: {} patches need update\n-- {}\n".format( 286 | out_dir, len(bad_patches), "\n-- ".join(bad_patches) 287 | ) 288 | ) 289 | sys.exit(1) 290 | else: 291 | # Remove old patches so that deleted commits are correctly reflected in the 292 | # patch files (as a removed file) 293 | for p in os.listdir(out_dir): 294 | if p.endswith('.patch'): 295 | os.remove(posixpath.join(out_dir, p)) 296 | with io.open( 297 | posixpath.join(out_dir, '.patches'), 298 | 'w', 299 | newline='\n', 300 | encoding='utf-8', 301 | ) as pl: 302 | for patch in patches: 303 | filename = get_file_name(patch) 304 | file_path = posixpath.join(out_dir, filename) 305 | formatted_patch = join_patch(patch) 306 | # Write in binary mode to retain mixed line endings on write. 307 | with io.open( 308 | file_path, 'wb' 309 | ) as f: 310 | f.write(formatted_patch.encode('utf-8')) 311 | pl.write(filename + '\n') 312 | -------------------------------------------------------------------------------- /.github/workflows/build-and-upload-engine-variants.yaml: -------------------------------------------------------------------------------- 1 | name: Build Flutter Engine with Engine Tool (Matrix) 2 | 3 | on: 4 | push: 5 | branches: [ "engine-builds" ] 6 | pull_request: 7 | branches: [ "engine-builds" ] 8 | env: 9 | # don't try to install google's internal copy of visual studio 10 | DEPOT_TOOLS_WIN_TOOLCHAIN: 0 11 | FLOCK_REPO: ${{ vars.FLOCK_REPO }} 12 | FLOCK_BRANCH: ${{ github.ref }} 13 | FLUTTER_BRANCH: master 14 | 15 | jobs: 16 | 17 | build-engine-web: 18 | # We'll run on whatever OS the matrix entry specifies. 19 | runs-on: ${{ matrix.os }} 20 | defaults: 21 | run: 22 | shell: bash 23 | 24 | strategy: 25 | max-parallel: 4 26 | matrix: 27 | include: 28 | 29 | - name: linux wasm release (web) 30 | config_name: wasm_release 31 | os: ubuntu-latest 32 | 33 | steps: 34 | - name: Check out the repository 35 | uses: actions/checkout@v4 36 | with: 37 | path: '' 38 | 39 | - name: assemble flock 40 | uses: ./.github/actions/assemble-flock 41 | with: 42 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 43 | 44 | - name: web build 45 | uses: ./.github/actions/build-and-upload-engine 46 | with: 47 | gcp_credentials: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 48 | gcp_bucket: flutter-builds 49 | gclient: slim-linux-web 50 | config_name: ${{ matrix.config_name }} 51 | engine_version: ${{ github.sha }} 52 | 53 | 54 | build-engine-android: 55 | # We'll run on whatever OS the matrix entry specifies. 56 | runs-on: ${{ matrix.os }} 57 | defaults: 58 | run: 59 | shell: bash 60 | 61 | strategy: 62 | max-parallel: 4 63 | matrix: 64 | include: 65 | - name: android debug 66 | os: ubuntu-latest 67 | config_name: ci/android_debug 68 | 69 | - name: android debug arm64 70 | os: ubuntu-latest 71 | config_name: ci/android_debug_arm64 72 | 73 | - name: android debug unopt 74 | os: ubuntu-latest 75 | config_name: ci/android_debug_unopt 76 | 77 | - name: android debug x64 78 | os: ubuntu-latest 79 | config_name: ci/android_debug_x64 80 | 81 | - name: android debug x86 82 | os: ubuntu-latest 83 | config_name: ci/android_debug_x86 84 | 85 | - name: android embedder debug unopt 86 | os: ubuntu-latest 87 | config_name: ci/android_embedder_debug_unopt 88 | 89 | - name: android emulator debug x64 90 | os: ubuntu-latest 91 | config_name: ci/android_emulator_debug_x64 92 | 93 | - name: android emulator debug x86 94 | os: ubuntu-latest 95 | config_name: ci/android_emulator_debug_x86 96 | 97 | - name: android profile 98 | os: ubuntu-latest 99 | config_name: ci/android_profile 100 | 101 | - name: android profile arm64 102 | os: ubuntu-latest 103 | config_name: ci/android_profile_arm64 104 | 105 | - name: android profile x64 106 | os: ubuntu-latest 107 | config_name: ci/android_profile_x64 108 | 109 | - name: android release 110 | os: ubuntu-latest 111 | config_name: ci/android_release 112 | 113 | - name: android release arm64 114 | os: ubuntu-latest 115 | config_name: ci/android_release_arm64 116 | 117 | - name: android release x64 118 | os: ubuntu-latest 119 | config_name: ci/android_release_x64 120 | 121 | steps: 122 | - name: Check out the repository 123 | uses: actions/checkout@v4 124 | 125 | - name: assemble flock 126 | uses: ./.github/actions/assemble-flock 127 | with: 128 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 129 | 130 | - name: android build 131 | uses: ./.github/actions/build-and-upload-engine 132 | with: 133 | gcp_credentials: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 134 | gcp_bucket: flutter-builds 135 | gclient: slim-linux-android 136 | config_name: ${{ matrix.config_name }} 137 | engine_version: ${{ github.sha }} 138 | 139 | 140 | build-engine-linux: 141 | # We'll run on whatever OS the matrix entry specifies. 142 | runs-on: ${{ matrix.os }} 143 | defaults: 144 | run: 145 | shell: bash 146 | 147 | 148 | strategy: 149 | max-parallel: 4 150 | matrix: 151 | include: 152 | 153 | - name: linux host debug unopt 154 | os: ubuntu-latest 155 | config_name: ci/host_debug_unopt 156 | 157 | - name: linux debug arm64 158 | os: ubuntu-latest 159 | config_name: ci/linux_debug_arm64 160 | 161 | - name: linux profile arm64 162 | os: ubuntu-latest 163 | config_name: ci/linux_profile_arm64 164 | - name: linux release arm64 165 | os: ubuntu-latest 166 | config_name: ci/linux_release_arm64 167 | 168 | - name: linux host release 169 | os: ubuntu-latest 170 | config_name: ci/host_release 171 | 172 | - name: linux host debug 173 | config_name: ci/host_debug 174 | os: ubuntu-latest 175 | 176 | 177 | - name: linux host release 178 | os: ubuntu-latest 179 | config_name: ci/host_release_desktop 180 | 181 | - name: linux host debug 182 | config_name: ci/host_debug_desktop 183 | os: ubuntu-latest 184 | 185 | - name: linux host debug clang tidy 186 | os: ubuntu-latest 187 | config_name: ci/host_debug_clang_tidy 188 | 189 | steps: 190 | 191 | - name: checkout out the repository 192 | uses: actions/checkout@v4 193 | 194 | - name: assemble flock 195 | uses: ./.github/actions/assemble-flock 196 | with: 197 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 198 | 199 | - name: linux build 200 | uses: ./.github/actions/build-and-upload-engine 201 | with: 202 | gcp_credentials: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 203 | gcp_bucket: flutter-builds 204 | gclient: slim-linux-desktop 205 | config_name: ${{ matrix.config_name }} 206 | engine_version: ${{ github.sha }} 207 | 208 | 209 | # ------------------------- TESTS BUILDS ------------------------- 210 | 211 | # - name: linux host debug test 212 | # os: ubuntu-latest 213 | # config_name: ci/host_debug_test 214 | # - name: linux host debug unopt impeller tests 215 | # os: ubuntu-latest 216 | # config_name: ci/host_debug_unopt_impeller_tests 217 | # - name: linux host profile desktop 218 | # os: ubuntu-latest 219 | # config_name: ci/host_profile_desktop 220 | #- name: linux host profile test 221 | # os: ubuntu-latest 222 | # config_name: ci/host_profile_test 223 | #- name: linux host release test 224 | # os: ubuntu-latest 225 | # config_name: ci/host_release_test 226 | #- name: android debug test 227 | # os: ubuntu-latest 228 | # config_name: ci/android_debug_test 229 | 230 | build-engine-mac: 231 | # We'll run on whatever OS the matrix entry specifies. 232 | runs-on: ${{ matrix.os }} 233 | defaults: 234 | run: 235 | shell: bash 236 | 237 | strategy: 238 | max-parallel: 4 239 | matrix: 240 | include: 241 | 242 | # ------------------------- MAC BUILDS ------------------------- 243 | 244 | - name: mac debug arm64 245 | os: macos-latest 246 | config_name: ci/mac_debug_arm64 247 | 248 | - name: mac debug framework arm64 249 | os: macos-latest 250 | config_name: ci/mac_debug_framework_arm64 251 | 252 | - name: mac debug gen snapshot arm64 253 | os: macos-latest 254 | config_name: ci/mac_debug_gen_snapshot_arm64 255 | 256 | - name: mac profile arm64 257 | os: macos-latest 258 | config_name: ci/mac_profile_arm64 259 | 260 | - name: mac profile frmaoek arm64 261 | os: macos-latest 262 | config_name: ci/mac_profile_framework_arm64 263 | 264 | - name: mac profile gen snapshot arm64 265 | os: macos-latest 266 | config_name: ci/mac_profile_gen_snapshot_arm64 267 | 268 | - name: mac release arm64 269 | os: macos-latest 270 | config_name: ci/mac_release_arm64 271 | 272 | - name: mac framework arm64 273 | os: macos-latest 274 | config_name: ci/mac_release_framework_arm64 275 | 276 | - name: mac mac release gen snapshot arm64 277 | os: macos-latest 278 | config_name: ci/mac_release_gen_snapshot_arm64 279 | 280 | - name: mac host debug clang tidy 281 | os: macos-latest 282 | config_name: ci/host_debug_clang_tidy 283 | 284 | - name: mac host debug framework 285 | os: macos-latest 286 | config_name: ci/host_debug_framework 287 | 288 | - name: mac host debug den snapshot 289 | os: macos-latest 290 | config_name: ci/host_debug_gen_snapshot 291 | 292 | - name: mac host debug unopt arm64 293 | os: macos-latest 294 | config_name: ci/host_debug_unopt_arm64 295 | 296 | - name: mac host profile framework 297 | os: macos-latest 298 | config_name: ci/host_profile_framework 299 | 300 | - name: mac host profile gen snapshot 301 | os: macos-latest 302 | config_name: ci/host_profile_gen_snapshot 303 | 304 | - name: mac host release framework 305 | os: macos-latest 306 | config_name: ci/host_release_framework 307 | 308 | - name: mac host release gen snapshot 309 | os: macos-latest 310 | config_name: ci/host_release_gen_snapshot 311 | 312 | - name: mac host profile 313 | os: macos-latest 314 | config_name: ci/host_profile 315 | 316 | - name: mac host release 317 | os: macos-latest 318 | config_name: ci/host_release 319 | 320 | - name: mac host debug 321 | config_name: ci/host_debug 322 | os: macos-latest 323 | 324 | - name: mac release framework 325 | config_name: ci/host_release_framework 326 | os: macos-latest 327 | 328 | - name: mac debug framework 329 | config_name: ci/host_debug_framework 330 | os: macos-latest 331 | 332 | steps: 333 | 334 | - name: Set up Xcode version 335 | uses: maxim-lobanov/setup-xcode@v1 336 | with: 337 | xcode-version: '16.2' 338 | 339 | - name: Check out the repository 340 | uses: actions/checkout@v4 341 | 342 | - name: assemble flock 343 | uses: ./.github/actions/assemble-flock 344 | with: 345 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 346 | 347 | - name: mac build 348 | uses: ./.github/actions/build-and-upload-engine 349 | with: 350 | gcp_credentials: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 351 | gcp_bucket: flutter-builds 352 | gclient: slim-mac 353 | config_name: ${{ matrix.config_name }} 354 | engine_version: ${{ github.sha }} 355 | 356 | 357 | build-engine-ios: 358 | # We'll run on whatever OS the matrix entry specifies. 359 | runs-on: ${{ matrix.os }} 360 | defaults: 361 | run: 362 | shell: bash 363 | 364 | strategy: 365 | max-parallel: 4 366 | matrix: 367 | include: 368 | # ------------------------- iOS BUILDS ------------------------- 369 | 370 | - name: ios debug 371 | config_name: ci/ios_debug 372 | os: macos-latest 373 | 374 | - name: ios debug sim 375 | config_name: ci/ios_debug_sim 376 | os: macos-latest 377 | 378 | - name: ios debug sim arm64 379 | config_name: ci/ios_debug_sim_arm64 380 | os: macos-latest 381 | 382 | - name: ios profile 383 | config_name: ci/ios_profile 384 | os: macos-latest 385 | 386 | - name: ios release 387 | config_name: ci/ios_release 388 | os: macos-latest 389 | 390 | - name: ios debug extension safe 391 | config_name: ci/ios_debug_extension_safe 392 | os: macos-latest 393 | 394 | - name: ios debug sim arm64 extension safe 395 | config_name: ci/ios_debug_sim_arm64_extension_safe 396 | os: macos-latest 397 | 398 | - name: ios debug sim extension safe 399 | config_name: ci/ios_debug_sim_extension_safe 400 | os: macos-latest 401 | 402 | - name: ios debug unopt sim arm64 extension safe 403 | config_name: ci/ios_debug_unopt_sim_arm64_extension_safe 404 | os: macos-latest 405 | 406 | - name: ios profile extension safe 407 | config_name: ci/ios_profile_extension_safe 408 | os: macos-latest 409 | 410 | - name: ios release extension safe 411 | config_name: ci/ios_release_extension_safe 412 | os: macos-latest 413 | steps: 414 | 415 | - name: Check out the repository 416 | uses: actions/checkout@v4 417 | 418 | - name: assemble flock 419 | uses: ./.github/actions/assemble-flock 420 | with: 421 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 422 | 423 | - name: mac build 424 | uses: ./.github/actions/build-and-upload-engine 425 | with: 426 | gcp_credentials: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 427 | gcp_bucket: flutter-builds 428 | gclient: slim-mac 429 | config_name: ${{ matrix.config_name }} 430 | engine_version: ${{ github.sha }} 431 | 432 | build-ios-release: 433 | needs: build-engine-ios 434 | runs-on: macos-latest 435 | defaults: 436 | run: 437 | shell: bash 438 | 439 | steps: 440 | 441 | - name: Set up Xcode version 442 | uses: maxim-lobanov/setup-xcode@v1 443 | with: 444 | xcode-version: '16.2' 445 | 446 | - name: 'Set up Cloud SDK' 447 | uses: 'google-github-actions/setup-gcloud@v2' 448 | with: 449 | version: '>= 363.0.0' 450 | 451 | - name: Setup gcloud 452 | env: 453 | GCP_CREDENTIALS: ${{ secrets.GOOGLE_STORAGE_BUCKET_SECRET }} 454 | run: | 455 | echo "$GCP_CREDENTIALS" | base64 -d > "$HOME"/gcloud.json 456 | gcloud auth activate-service-account --key-file="$HOME"/gcloud.json 457 | 458 | - uses: actions/setup-python@v5 459 | with: 460 | python-version: '3.13' 461 | 462 | - name: Set up depot_tools / gclient 463 | run: | 464 | git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $HOME/depot_tools 465 | # Append depot_tools to the PATH for subsequent steps 466 | echo "$HOME/depot_tools" >> $GITHUB_PATH 467 | 468 | - name: Check out the repository 469 | uses: actions/checkout@v4 470 | 471 | - name: assemble flock 472 | uses: ./.github/actions/assemble-flock 473 | with: 474 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 475 | 476 | - name: gclient sync macos 477 | run: | 478 | # Use custom gclient to trim size 479 | cd flock 480 | cp engine/scripts/slim-mac.gclient .gclient 481 | gclient sync -D 482 | 483 | - name: build ios release 484 | run: | 485 | 486 | cd flock/engine/src 487 | 488 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_profile.tar.gz' . 489 | tar -xzf ios_profile.tar.gz 490 | rm ios_profile.tar.gz 491 | 492 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug.tar.gz' . 493 | tar -xzf ios_debug.tar.gz 494 | rm ios_debug.tar.gz 495 | 496 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_release.tar.gz' . 497 | tar -xzf ios_release.tar.gz 498 | rm ios_release.tar.gz 499 | 500 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_sim.tar.gz' . 501 | tar -xzf ios_debug_sim.tar.gz 502 | rm ios_debug_sim.tar.gz 503 | 504 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_sim_arm64.tar.gz' . 505 | tar -xzf ios_debug_sim_arm64.tar.gz 506 | rm ios_debug_sim_arm64.tar.gz 507 | 508 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_release_extension_safe.tar.gz' . 509 | tar -xzf ios_release_extension_safe.tar.gz 510 | rm ios_release_extension_safe.tar.gz 511 | 512 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_extension_safe.tar.gz' . 513 | tar -xzf ios_debug_extension_safe.tar.gz 514 | rm ios_debug_extension_safe.tar.gz 515 | 516 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_sim_arm64_extension_safe.tar.gz' . 517 | tar -xzf ios_debug_sim_arm64_extension_safe.tar.gz 518 | rm ios_debug_sim_arm64_extension_safe.tar.gz 519 | 520 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_unopt_sim_arm64_extension_safe.tar.gz' . 521 | tar -xzf ios_debug_unopt_sim_arm64_extension_safe.tar.gz 522 | rm ios_debug_unopt_sim_arm64_extension_safe.tar.gz 523 | 524 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_debug_sim_extension_safe.tar.gz' . 525 | tar -xzf ios_debug_sim_extension_safe.tar.gz 526 | rm ios_debug_sim_extension_safe.tar.gz 527 | 528 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_profile_extension_safe.tar.gz' . 529 | tar -xzf ios_profile_extension_safe.tar.gz 530 | rm ios_profile_extension_safe.tar.gz 531 | 532 | gcloud storage cp --recursive 'gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ci/ios_release_extension_safe.tar.gz' . 533 | tar -xzf ios_release_extension_safe.tar.gz 534 | rm ios_release_extension_safe.tar.gz 535 | 536 | 537 | python3 flutter/sky/tools/create_ios_framework.py \ 538 | --dst out/ios \ 539 | --arm64-out-dir out/ci/ios_debug \ 540 | --simulator-x64-out-dir out/ci/ios_debug_sim \ 541 | --simulator-arm64-out-dir out/ci/ios_debug_sim_arm64 \ 542 | --dsym \ 543 | --strip 544 | 545 | python3 flutter/sky/tools/create_ios_framework.py \ 546 | --dst out/ios_release \ 547 | --arm64-out-dir out/ci/ios_release \ 548 | --simulator-x64-out-dir out/ci/ios_debug_sim \ 549 | --simulator-arm64-out-dir out/ci/ios_debug_sim_arm64 \ 550 | --dsym \ 551 | --strip \ 552 | 553 | python3 flutter/sky/tools/create_ios_framework.py \ 554 | --dst out/ios_profile \ 555 | --arm64-out-dir out/ci/ios_profile \ 556 | --simulator-x64-out-dir out/ci/ios_debug_sim \ 557 | --simulator-arm64-out-dir out/ci/ios_debug_sim_arm64 \ 558 | --dsym \ 559 | --strip \ 560 | 561 | gcloud storage cp --recursive out/ios/ gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ios/ 562 | gcloud storage cp --recursive out/ios_release/ gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ios_release/ 563 | gcloud storage cp --recursive out/ios_profile/ gs://flutter-builds/flutter_infra_release/flutter/engine/${{github.sha}}/ios_profile/ 564 | 565 | 566 | # after we build the engine successfully, it's ok to push our branch 567 | push-build: 568 | # needs: build-engine-ios 569 | runs-on: ubuntu-latest 570 | defaults: 571 | run: 572 | shell: bash 573 | 574 | steps: 575 | - name: check out the repository 576 | uses: actions/checkout@v4 577 | 578 | - name: assemble flock 579 | uses: ./.github/actions/assemble-flock 580 | with: 581 | flutter_ref: ${{ env.FLUTTER_BRANCH }} 582 | token: ${{ secrets.REPO_WORKFLOW_PAT }} 583 | 584 | # Compute an engine version hash so the flutter tool will be able to find a precompiled engine 585 | - name: save engine hash 586 | shell: bash 587 | run: | 588 | cd flock 589 | echo "${{ github.sha }}" > ./bin/internal/engine.version 590 | git add -f ./bin/internal/engine.version 591 | git config --global user.email "no-reply@getflocked.dev" 592 | git config --global user.name "Flock" 593 | git commit -m "save engine version" --allow-empty 594 | 595 | - name: update our remote flock branch to use this engine hash 596 | uses: ./.github/actions/push-flock-repo 597 | with: 598 | destination_repo: ${{ env.FLOCK_REPO }} 599 | destination_branch: ${{ env.FLOCK_BRANCH }} --------------------------------------------------------------------------------