diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..904b21e0 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,55 @@ +# Add 'root' label to any root file changes +# Quotation marks are required for the leading asterisk +root: +- changed-files: + - any-glob-to-any-file: '*' + +# Add 'AnyChange' label to any changes within the entire repository +AnyChange: +- changed-files: + - any-glob-to-any-file: '**' + +DevOps: +- changed-files: + - any-glob-to-any-file: + - '.github/*' +# Add 'Documentation' label to any file changes within 'docs' or 'guides' folders +Documentation: +- changed-files: + - any-glob-to-any-file: + - doc/* + - '**/*.md' +- head-branch: ['^doc', 'doc'] + +Dotnet: + - changed-files: + - any-glob-to-any-file: + - dotnet/* + +BirdApi: +- changed-files: + - any-glob-to-any-file: + - dotnet/BirdApi/* + +UserApi: +- changed-files: + - any-glob-to-any-file: + - dotnet/BirdApi/* + +flutter: +- changed-files: + - any-glob-to-any-file: + - beakpeek/* + +# Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name +feature: +- head-branch: ['^dev/feat', 'feat'] + +fix: + - head-branch: ['^dev/fix', 'fix'] + +test: + - head-branch: ['^dev/test', 'test'] + +release: + - base-branch: 'main' diff --git a/.github/workflows/deploy_dotnet.yml b/.github/workflows/deploy_dotnet.yml index 219b0e7c..194dcc91 100644 --- a/.github/workflows/deploy_dotnet.yml +++ b/.github/workflows/deploy_dotnet.yml @@ -1,7 +1,4 @@ -# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# More GitHub Actions for Azure: https://github.com/Azure/actions - -name: Deploy BeakPeekBirdApi +name: ๐Ÿš€ Deploy Dotnet BirdApi env: # DOTNET_ROOT: ./dotnet/BeakPeekApi/ @@ -18,29 +15,42 @@ on: - 'beakpeek/**' - 'dotnet/UserApi/**' workflow_dispatch: + workflow_call: jobs: - test: + test_dotnet: + name: ๐Ÿงช Test dotnet BirdApi uses: ./.github/workflows/dotnet.yml + secrets: inherit build: + name: ๐Ÿ›๏ธ Build BirdApi runs-on: ubuntu-latest - # needs: test - + needs: [ test_dotnet ] + env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/birdapi/packages + outputs: + artifact_url: ${{ steps.artifact-upload-step.outputs.artifact-url}} steps: - uses: actions/checkout@v4 - name: Set up .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: '8.x' - include-prerelease: true + cache: true + cache-dependency-path: ./dotnet/BirdApi/BeakPeekApi/packages.lock.json - name: Get App Setting run: | cd ./dotnet/BirdApi/BeakPeekApi cp ../../../appsettings.json . + - name: Restore Dependencies + run: | + cd ./dotnet/BirdApi/BeakPeekApi + dotnet restore --locked-mode + - name: Build with dotnet run: dotnet build --configuration Release ./dotnet/BirdApi/BeakPeekApi/BeakPeekApi.csproj @@ -55,14 +65,16 @@ jobs: cp -r ./res/species_list/* ${{env.DOTNET_ROOT}}/BeakPeekApi/res/species_list/ - name: Upload artifact for deployment job - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 + id: artifact-upload-step with: - name: .net-app + name: dotnet-birdapi path: ${{env.DOTNET_ROOT}}/BeakPeekApi deploy: runs-on: ubuntu-latest - needs: build + name: ๐Ÿš€ Deploy BirdApi + needs: [ build ] environment: name: 'Production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} @@ -71,10 +83,10 @@ jobs: steps: - name: Download artifact from build job - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: .net-app - + name: dotnet-birdapi + - name: Login to Azure uses: azure/login@v1 with: @@ -89,4 +101,16 @@ jobs: app-name: 'BeakPeekBirdApi' slot-name: 'Production' package: . - + + notify_discord: + name: ๐Ÿ”” Send Discord notification about deployment + needs: [deploy, build] + if: ${{ !cancelled() && (success() || failure()) }} + uses: ./.github/workflows/discord.yml + with: + content: "${{ contains(needs.deploy.result, 'success') && 'Successfully deployed' || 'Error during deployment of' }} ${{ github.ref_name }} for Azure" + title: "${{ contains(needs.deploy.result, 'success') && 'Successfully deployed' || 'Error during deployment of' }} ${{ github.ref_name }} for Azure" + url: ${{ needs.build.outputs.artifact_url }} + description: "${{ contains(needs.deploy.result, 'success') && 'Deployed:' || 'Deployment failed:' }} ${{ github.event.head_commit.message }}" + color: ${{ contains(needs.deploy.result, 'success') && 65280 || 16711680 }} + secrets: inherit diff --git a/.github/workflows/deploy_flutter.yml b/.github/workflows/deploy_flutter.yml new file mode 100644 index 00000000..3f05bb26 --- /dev/null +++ b/.github/workflows/deploy_flutter.yml @@ -0,0 +1,128 @@ +name: ๐Ÿ“ข Publish Android release + +on: + release: + types: [published] + # push: + # branches: + # - 'dev/feat/devops' + workflow_dispatch: + workflow_call: + +env: + FLUTTER_CHANNEL: "stable" + RUBY_VERSION: "3.2.2" + +jobs: + test_android: + name: ๐Ÿงช Test Flutter + uses: ./.github/workflows/flutter.yml + secrets: inherit + + build_android: + name: ๐Ÿ›๏ธ Build Android + needs: [test_android] + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./beakpeek + timeout-minutes: 20 + outputs: + artifact_url: ${{ steps.artifact-upload-step.outputs.artifact-url}} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + if: ${{ env.ACT }} + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Android SDK + if: ${{ env.ACT }} + uses: android-actions/setup-android@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + working-directory: './beakpeek/android' + + - name: Run Flutter tasks + uses: subosito/flutter-action@v2.16.0 + with: + channel: stable + cache: true + pub-cache-key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + + - name: Create google_service_account.json + run: | + pwd + echo "${{ secrets.FIREBASE_SERVICE_ACCOUNT_BASE64 }}" | base64 --decode > google_service_account.json + + - name: Create key.jks + run: | + pwd + echo "${{ secrets.ANDROID_KEYSTORE_FILE_BASE64 }}" | base64 --decode > android/app/upload-keystore.jks + + - name: Create local.properties + run: | + pwd + echo "${{ secrets.ANDROID_LOCAL_PROPERTIES_FILE_BASE64 }}" | base64 --decode > android/local.properties + + - name: Create key.properties + run: | + echo $PATH + pwd + cat < android/key.properties + storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyPassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyAlias=upload + storeFile=\\\upload-keystore.jks + EOF + env: + ANDROID_KEY_STORE_PASSWORD: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + + # - name: Cache Flutter dependancies + # uses: actions/cache@v3 + # with: + # path: | + # ~/.pub-cache + # ~/.flutter + # key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + # restore-keys: | + # ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + + - name: Build and Release to Google Play + uses: maierj/fastlane-action@v3.1.0 + with: + # lane: 'release_play_store' + lane: 'build' + subdirectory: ./beakpeek/android + options: '{ "version_number": "${{ github.ref_name }}" }' + env: + APP_PACKAGE_NAME: ${{ secrets.APP_PACKAGE_NAME }} + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + id: artifact-upload-step + with: + name: app-release + path: beakpeek/build/app/outputs/bundle/release/app-release.aab + + notify_discord: + name: ๐Ÿ”” Send Discord notification about Build + needs: [build_android] + if: ${{ !cancelled() && (success() || failure()) }} + uses: ./.github/workflows/discord.yml + with: + content: "${{ contains(needs.build_android.result, 'success') && 'Successfully released' || 'Error during release of' }} ${{ github.ref_name }} for Android to Play Store" + title: "${{ contains(needs.build_android.result, 'success') && 'Successfully released' || 'Error during release of' }} ${{ github.ref_name }} for Android to Play Store" + url: ${{ needs.build_android.outputs.artifact_url }} + description: "${{ contains(needs.build_android.result, 'success') && 'Released:' || 'Release failed:' }} ${{ github.event.head_commit.message }}" + color: ${{ contains(needs.build_android.result, 'success') && 65280 || 16711680 }} + secrets: inherit + diff --git a/.github/workflows/deploy_userapi.yml b/.github/workflows/deploy_userapi.yml index 51a67fa0..7590d4d4 100644 --- a/.github/workflows/deploy_userapi.yml +++ b/.github/workflows/deploy_userapi.yml @@ -1,78 +1,102 @@ -# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# More GitHub Actions for Azure: https://github.com/Azure/actions - -name: Deploy Dotnet UserApi +name: ๐Ÿš€ Deploy Dotnet UserApi -# env: - # DOTNET_ROOT: ./dotnet/UserApi/UserApi -on: - push: - branches: - - main +env: + DOTNET_ROOT: . + +on: + push: + branches: + - main - development paths-ignore: - 'doc/**' - 'res/**' - 'beakpeek/**' - 'dotnet/BirdApi/**' - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.x' - + workflow_dispatch: + workflow_call: + +jobs: + build: + name: ๐Ÿ›๏ธ Build UserApi + runs-on: ubuntu-latest + env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/userapi/packages + outputs: + artifact_url: ${{ steps.artifact-upload-step.outputs.artifact-url}} + steps: + - uses: actions/checkout@v4 + + - name: Set up .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + cache: true + cache-dependency-path: ./dotnet/UserApi/UserApi/packages.lock.json + - name: Get App Setting run: | cd ./dotnet/UserApi/UserApi cp ../../../appsettings.json . - - name: Build with dotnet + - name: Restore Dependencies + run: | + cd ./dotnet/UserApi/UserApi + dotnet restore --locked-mode + + - name: Build with dotnet run: dotnet build --configuration Release ./dotnet/UserApi/UserApi/UserApi.csproj - - - name: dotnet publish + + - name: dotnet publish run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/UserApi ./dotnet/UserApi/UserApi/UserApi.csproj - - - name: Upload artifact for deployment job - uses: actions/upload-artifact@v4 - with: - name: .net-app + + - name: Upload artifact for deployment job + id: artifact-upload-step + uses: actions/upload-artifact@v4 + with: + name: dotnet-userapi path: ${{env.DOTNET_ROOT}}/UserApi - - deploy: - runs-on: ubuntu-latest - needs: build - environment: - name: 'Production' - url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + + deploy: + runs-on: ubuntu-latest + name: ๐Ÿš€ Deploy UserApi + needs: [ build ] + environment: + name: 'Production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} permissions: id-token: write #This is required for requesting the JWT - - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v4 - with: - name: .net-app - + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v4 + with: + name: dotnet-userapi + - name: Login to Azure uses: azure/login@v2 with: client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_B556361464624DEBB42BF2DC59698DCF }} tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_C8847AFCA0AA461FBC4F817E352AF220 }} subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_EE92CF8E523348EBBA87A7F4C8B0D4DA }} - - - name: Deploy to Azure Web App - id: deploy-to-webapp - uses: azure/webapps-deploy@v3 - with: - app-name: 'BeakPeekUserApi' - slot-name: 'Production' - package: . - + + - name: Deploy to Azure Web App + id: deploy-to-webapp + uses: azure/webapps-deploy@v3 + with: + app-name: 'BeakPeekUserApi' + slot-name: 'Production' + package: . + + notify_discord: + name: ๐Ÿ”” Send Discord notification about deployment + needs: [ deploy, build] + if: ${{ !cancelled() && (success() || failure()) }} + uses: ./.github/workflows/discord.yml + with: + content: "${{ contains(needs.deploy.result, 'success') && 'Successfully deployed' || 'Error during deployment of' }} ${{ github.ref_name }} for Azure" + title: "${{ contains(needs.deploy.result, 'success') && 'Successfully deployed' || 'Error during deployment of' }} ${{ github.ref_name }} for Azure" + url: ${{ needs.build.outputs.artifact_url }} + description: "${{ contains(needs.deploy.result, 'success') && 'Deployed:' || 'Deployment failed:' }} ${{ github.event.head_commit.message }}" + color: ${{ contains(needs.deploy.result, 'success') && 65280 || 16711680 }} + secrets: inherit diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 00000000..798ad106 --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,55 @@ +name: ๐Ÿ”” Notify discord + +on: + # push: + workflow_dispatch: + workflow_call: + inputs: + content: + type: string + required: true + title: + type: string + required: true + url: + type: string + required: true + description: + type: string + required: true + color: + type: number + required: true + +jobs: + notify: + name: ๐Ÿ”” Send Discord notification about Build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set current time and date + run: | + echo "NOW=$(date +'%Y-%m-%dT%H:%M:%S%z')" >> $GITHUB_ENV + + - name: ๐Ÿ”” Notify discord + uses: tsickert/discord-webhook@v5.3.0 + with: + webhook-url: ${{ secrets.WEBHOOK_URL }} + username: Android Deploy + avatar-url: https://github.com/COS301-SE-2024/BeakPeek/blob/main/res/Logo.png?raw=true + content: ${{ inputs.content }} + embed-title: ${{ inputs.title }} + embed-url: ${{ inputs.url }} + embed-description: ${{ inputs.description }} + embed-author-name: "github" + embed-author-icon-url: https://github.com/COS301-SE-2024/BeakPeek/blob/main/res/Logo.png?raw=true + embed-author-url: "https://github.com/COS301-SE-2024/BeakPeek" + embed-thumbnail-url: "https://github.com/COS301-SE-2024/BeakPeek/blob/main/res/Logo.png?raw=true" + embed-image-url: "https://github.com/COS301-SE-2024/BeakPeek/blob/main/res/Logo.png?raw=true" + embed-footer-icon-url: "https://github.com/COS301-SE-2024/BeakPeek/blob/main/res/Logo.png?raw=true" + embed-timestamp: ${{ env.NOW }} + embed-color: ${{ inputs.color }} + embed-footer-text: "DevOps" + wait: true diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bc04bda4..ba46b522 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,4 +1,4 @@ -name: Test Dotnet and Upload Code Coverage +name: ๐Ÿฅ… Test Dotnet and Upload Code Coverage on: push: @@ -16,9 +16,13 @@ on: - dotnet/BirdApi/** workflow_dispatch: workflow_call: + secrets: + CODECOV_TOKEN: + required: true jobs: dotnet-codecov: + name: ๐Ÿงช Test and Upload dotnet runs-on: ubuntu-latest env: diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index 535b8597..b706b24d 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -1,4 +1,4 @@ -name: CICD +name: ๐Ÿฆ Flutter tests and coverage on: push: branches: @@ -14,6 +14,10 @@ on: paths: - beakpeek/** workflow_dispatch: + workflow_call: + secrets: + CODECOV_TOKEN: + required: true defaults: run: @@ -21,25 +25,18 @@ defaults: jobs: Run-Flutter-Tests: + name: ๐Ÿงช Test flutter runs-on: ubuntu-latest steps: - name: Clone repository uses: actions/checkout@v4 - name: Set up Flutter - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@v2.16.0 with: channel: stable - - - name: Cache Flutter dependancies - uses: actions/cache@v3 - with: - path: | - ~/.pub-cache - ~/.flutter - keys: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} - restore-keys: | - ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + cache: true + pub-cache-key: ${{hashFiles('**/pubspec.yaml')}} - name: Install Flutter dependecies run: flutter pub get @@ -48,6 +45,7 @@ jobs: run: flutter test --coverage - name: Upload Flutter Coverage to Codecov + if: ${{ !env.ACT }} uses: codecov/codecov-action@v3 with: files: ./beakpeek/coverage/lcov.info diff --git a/.github/workflows/full_pipeline.yml b/.github/workflows/full_pipeline.yml new file mode 100644 index 00000000..498a918a --- /dev/null +++ b/.github/workflows/full_pipeline.yml @@ -0,0 +1,40 @@ +name: ๐Ÿš€ Run full CICD pipeline + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + flutter: + name: ๐Ÿฆ Flutter pipeline + uses: ./.github/workflows/deploy_flutter.yml + secrets: inherit + + birdapi: + name: ๐Ÿฆœ Dotnet BirdApi pipeline + uses: ./.github/workflows/deploy_dotnet.yml + secrets: inherit + permissions: + id-token: write #This is required for requesting the JWT + + userapi: + name: ๐Ÿชช Dotnet UserApi pipeline + uses: ./.github/workflows/deploy_userapi.yml + secrets: inherit + permissions: + id-token: write #This is required for requesting the JWT + + notify_discord: + name: ๐Ÿ”” Notify Discord + needs: [flutter, userapi, birdapi] + uses: ./.github/workflows/discord.yml + with: + content: "All Workflows Passed" + title: "Full Pipeline Run" + url: "https://github.com/COS301-SE-2024/BeakPeek" + description: "Run all pipelines" + color: 111111 + secrets: inherit + diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..5b02cdc3 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,17 @@ +name: ๐Ÿ”€ Label Pull Requests + +on: + schedule: + - cron: "0 8-23 * * *" + + pull_request: + workflow_dispatch: + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 diff --git a/beakpeek/.gitignore b/beakpeek/.gitignore index 36c987a2..17317b1f 100644 --- a/beakpeek/.gitignore +++ b/beakpeek/.gitignore @@ -10,7 +10,8 @@ .svn/ migrate_working_dir/ windows/** - +linux/** +macos/** # IntelliJ related *.iml *.ipr @@ -48,3 +49,11 @@ app.*.map.json /test/bird_sheet_test.dart /test/Map/map_info_test.dart /test/Map/map_test.dart +.env +*.jks +key.properties +google_service_account.json +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/screenshots +**/fastlane/test_output diff --git a/beakpeek/Fastfile b/beakpeek/Fastfile new file mode 100644 index 00000000..cb37a7e8 --- /dev/null +++ b/beakpeek/Fastfile @@ -0,0 +1,121 @@ +opt_out_usage + +# Have an easy way to get the root of the project +def root_path + Dir.pwd.sub(/.*\Kfastlane/, '').sub(/.*\Kandroid/, '').sub(/.*\Kios/, '').sub(/.*\K\/\//, '') +end + +# Have an easy way to run flutter tasks on the root of the project +lane :sh_on_root do |options| + command = options[:command] + sh("cd #{root_path} && #{command}") +end + +# Tasks to be reused on each platform flow +lane :fetch_dependencies do + sh_on_root(command: "flutter pub get --suppress-analytics") +end + +# Tasks to be reused on each platform flow +lane :build_autogenerated_code do + sh_on_root(command: "flutter pub run build_runner build --delete-conflicting-outputs") +end + +# Tasks to be reused on each platform flow +lane :lint do + sh_on_root(command: "flutter format --suppress-analytics --set-exit-if-changed -n lib/main.dart lib/src/ test/") +end + +lane :build_flutter_app do |options| + pubspec_version_number = get_version_from_pubspec() + + type = options[:type] + build_number = options[:build_number] || get_build_number(options[:store]) + version_number = options[:version_number] || pubspec_version_number + no_codesign = options[:no_codesign] || false + config_only = options[:config_only] || false + commit = last_git_commit + + command = "flutter build #{type} --release --no-pub --suppress-analytics" + command += " --build-number=#{build_number}" if build_number.to_s != "" + command += " --build-name=#{version_number}" if version_number.to_s != "" + command += " --no-codesign" if no_codesign + command += " --config-only" if config_only + + UI.message("Building #{type} - version: #{version_number} - build: #{build_number} - commit: #{commit[:abbreviated_commit_hash]}") + + fetch_dependencies + + # Check if build_runner exists in pubspec.yaml + # If it does, run the build_runner command + if File.exist?("#{root_path}/pubspec.yaml") + pubspec_content = File.read("#{root_path}/pubspec.yaml") + if pubspec_content.include?("build_runner:") + build_autogenerated_code + end + end + + sh_on_root(command: command) +end + +# Tasks to be reused on each platform flow +lane :test do |options| + sh_on_root(command: "flutter test --no-pub --coverage --suppress-analytics") +end + + +# Private lane to verify all environment variables are set +private_lane :verify_env do |options| + # array of ENVS to check + envs = options.fetch(:envs, []) + + envs.each do |env| + if ENV[env].nil? || ENV[env].empty? + UI.user_error!("ENV \"#{env}\" is not set. Please set it in your environment variables (e.g. ios/fastlane/.env)") + end + end +end + +# A helper method to get the path to the firebase service account json file +def google_service_account_json_path + root_path + '/' + (ENV["GOOGLE_SERVICE_ACCOUNT_JSON_PATH"] || 'google_service_account.json') +end + +# Build number is a unique identifier for each build that is uploaded to the app store. +# This method will get the latest build number from the app store and increment it by 1. +# Ensure authenticate_apple_store is called before this method +def get_build_number(store) + return get_new_build_number( + bundle_identifier: store == "appstore" ? ENV["APP_BUNDLE_ID"] : nil, + package_name: store == "playstore" ? ENV["APP_PACKAGE_NAME"] : nil, + google_play_json_key_path: google_service_account_json_path + ).to_s +end + + +def get_version_from_pubspec + require 'yaml' + + # Define the correct path to pubspec.yaml relative to the Fastlane directory + pubspec_path = File.expand_path("#{root_path}/pubspec.yaml") + + # Check if the file exists to avoid errors + unless File.exist?(pubspec_path) + UI.error("pubspec.yaml file not found at path: #{pubspec_path}") + return nil + end + + # Parse the pubspec.yaml file + pubspec_content = File.read(pubspec_path) + + # Use regex to find the version number line and extract both version number and build number + version_line = pubspec_content.match(/version:\s*(\d+\.\d+\.\d+)\+(\d+)/) + if version_line + version_number = version_line[1] + else + UI.error("Version number not found in pubspec.yaml") + return nil + end + + return version_number +end diff --git a/beakpeek/android/Gemfile b/beakpeek/android/Gemfile new file mode 100644 index 00000000..84a4b3cf --- /dev/null +++ b/beakpeek/android/Gemfile @@ -0,0 +1,8 @@ +source "https://rubygems.org" + +gem "fastlane" +# takes version from flutter (pubspec.yaml) +gem "fastlane-plugin-flutter_version", git: "https://github.com/tianhaoz95/fastlane-plugin-flutter-version" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/beakpeek/android/Gemfile.lock b/beakpeek/android/Gemfile.lock new file mode 100644 index 00000000..b715ba52 --- /dev/null +++ b/beakpeek/android/Gemfile.lock @@ -0,0 +1,236 @@ +GIT + remote: https://github.com/tianhaoz95/fastlane-plugin-flutter-version + revision: aa92b678f4fe842d8c1428e45cea4cba837fdfea + specs: + fastlane-plugin-flutter_version (1.1.15) + +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.3.0) + aws-partitions (1.991.0) + aws-sdk-core (3.209.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.9) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.94.0) + aws-sdk-core (~> 3, >= 3.207.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.167.0) + aws-sdk-core (~> 3, >= 3.207.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.10.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.4) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + fastimage (2.3.1) + fastlane (2.224.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored (~> 1.2) + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (>= 0.1.1, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-plugin-firebase_app_distribution (0.9.1) + google-apis-firebaseappdistribution_v1 (~> 0.3.0) + google-apis-firebaseappdistribution_v1alpha (~> 0.2.0) + fastlane-plugin-get_new_build_number (0.3.1) + fastlane-plugin-firebase_app_distribution + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-firebaseappdistribution_v1 (0.3.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-firebaseappdistribution_v1alpha (0.2.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.1) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.7) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.7.2) + jwt (2.9.3) + base64 + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.15.0) + multipart-post (2.4.1) + nanaimo (0.3.0) + naturally (2.2.1) + nkf (0.2.0) + optparse (0.5.0) + os (1.1.4) + plist (3.7.1) + public_suffix (6.0.1) + rake (13.2.1) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.3.8) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.5) + signet (0.19.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.6.0) + word_wrap (1.0.0) + xcodeproj (1.25.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + fastlane + fastlane-plugin-firebase_app_distribution + fastlane-plugin-flutter_version! + fastlane-plugin-get_new_build_number + +BUNDLED WITH + 2.5.16 diff --git a/beakpeek/android/app/build.gradle b/beakpeek/android/app/build.gradle index 6905e6f6..db18e5a6 100644 --- a/beakpeek/android/app/build.gradle +++ b/beakpeek/android/app/build.gradle @@ -12,7 +12,7 @@ if (localPropertiesFile.exists()) { } } -def googleMapsApiKey = localProperties.getProperty('GOOGLE_MAPS_API_KEY') +def googleMapsApiKey = localProperties.getProperty("GOOGLE_MAPS_API_KEY") def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { @@ -25,7 +25,7 @@ if (flutterVersionName == null) { } def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') +def keystorePropertiesFile = rootProject.file("key.properties") if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } @@ -43,10 +43,10 @@ android { signingConfigs { release { - keyAlias = keystoreProperties['keyAlias'] - keyPassword = keystoreProperties['keyPassword'] - storeFile = file(keystoreProperties['storeFile']) - storePassword = keystoreProperties['storePassword'] + keyAlias = keystoreProperties["keyAlias"] + keyPassword = keystoreProperties["keyPassword"] + storeFile = file(keystoreProperties["storeFile"]) + storePassword = keystoreProperties["storePassword"] } } @@ -55,17 +55,14 @@ android { minSdk = 24 targetSdk = 34 resValue "string", "google_maps_key", googleMapsApiKey - versionCode 7 - versionName "2.0" + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName multiDexEnabled true } buildTypes { release { signingConfig signingConfigs.release - minifyEnabled false - shrinkResources false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } diff --git a/beakpeek/android/fastlane/Appfile b/beakpeek/android/fastlane/Appfile new file mode 100644 index 00000000..50946aba --- /dev/null +++ b/beakpeek/android/fastlane/Appfile @@ -0,0 +1 @@ +package_name(ENV["APP_PACKAGE_NAME"]) # e.g. com.krausefx.app diff --git a/beakpeek/android/fastlane/Fastfile b/beakpeek/android/fastlane/Fastfile new file mode 100644 index 00000000..508cf57b --- /dev/null +++ b/beakpeek/android/fastlane/Fastfile @@ -0,0 +1,85 @@ +import "../../Fastfile" + +default_platform(:android) + +platform :android do + + # Build flutter Android app + lane :build do |options| + verify_env(envs: [ + "APP_PACKAGE_NAME" + ]) + + # Verify 'firebase_app_distribution_service_account.json' file exists + unless File.exist?(google_service_account_json_path) + UI.user_error!("google_service_account.json file not found. Please add it to the root of the flutter project. See https://docs.fastlane.tools/actions/supply/") + end + + # Verify version number is correct + if !is_ci && (!options[:version_number]) + version_number = get_version_from_pubspec() + continue = UI.confirm("Deploying version #{version_number} (from pubspec.yaml) to Play Store. Continue?") + + unless continue + UI.user_error!("Aborted") + end + end + + build_flutter_app( + type: options[:type] || "appbundle", + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number], + store: "playstore" + ) + end + + # Release to Play Store using Fastlane Supply (https://docs.fastlane.tools/actions/supply/) + desc "Release to Play Store" + lane :release_play_store do |options| + begin + build( + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number] + ) + + supply( + track: 'internal', + # Uncomment this if getting error "Only releases with status draft may be created on draft app." + # release_status: 'draft', + aab: "../build/app/outputs/bundle/release/app-release.aab", + json_key: google_service_account_json_path, + skip_upload_apk: true, # Upload the aab instead of apk + skip_upload_metadata: true, + skip_upload_changelogs: true, + skip_upload_images: true, + skip_upload_screenshots: true + ) + end + end + + # Release to Play Store using Firebase App Distribution (https://docs.fastlane.tools/actions/firebase_app_distribution/) + desc "Release to Play Store using Firebase App Distribution" + lane :release_play_store_using_firebase do |options| + begin + build( + type: 'apk', + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number] + ) + + firebase_app_distribution( + app: ENV["FIREBASE_APP_ID"], + android_artifact_path: "#{root_path}/build/app/outputs/flutter-apk/app-release.apk", + service_credentials_file: google_service_account_json_path, + # Use the following to enable debug mode + debug: true + ) + end + end +end \ No newline at end of file diff --git a/beakpeek/android/fastlane/Pluginfile b/beakpeek/android/fastlane/Pluginfile new file mode 100644 index 00000000..4a109fc2 --- /dev/null +++ b/beakpeek/android/fastlane/Pluginfile @@ -0,0 +1,6 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-firebase_app_distribution' +gem 'fastlane-plugin-get_new_build_number' diff --git a/beakpeek/android/fastlane/README.md b/beakpeek/android/fastlane/README.md new file mode 100644 index 00000000..559d9070 --- /dev/null +++ b/beakpeek/android/fastlane/README.md @@ -0,0 +1,99 @@ +fastlane documentation +---- + +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +```sh +xcode-select --install +``` + +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) + +# Available Actions + +### sh_on_root + +```sh +[bundle exec] fastlane sh_on_root +``` + + + +### fetch_dependencies + +```sh +[bundle exec] fastlane fetch_dependencies +``` + + + +### build_autogenerated_code + +```sh +[bundle exec] fastlane build_autogenerated_code +``` + + + +### lint + +```sh +[bundle exec] fastlane lint +``` + + + +### build_flutter_app + +```sh +[bundle exec] fastlane build_flutter_app +``` + + + +### test + +```sh +[bundle exec] fastlane test +``` + + + +---- + + +## Android + +### android build + +```sh +[bundle exec] fastlane android build +``` + + + +### android release_play_store + +```sh +[bundle exec] fastlane android release_play_store +``` + +Release to Play Store + +### android release_play_store_using_firebase + +```sh +[bundle exec] fastlane android release_play_store_using_firebase +``` + +Release to Play Store using Firebase App Distribution + +---- + +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/beakpeek/bootstrap_fastlane.dart b/beakpeek/bootstrap_fastlane.dart new file mode 100644 index 00000000..a452f0b8 --- /dev/null +++ b/beakpeek/bootstrap_fastlane.dart @@ -0,0 +1,727 @@ +// ignore_for_file: avoid_print + +import 'dart:io'; + +void main() { + // Read the PRODUCT_BUNDLE_IDENTIFIER from ios/Runner.xcodeproj/project.pbxproj + // or fallback to com.example.app + String? appBundleId = 'com.example.app'; + File iosProjectFile = File('ios/Runner.xcodeproj/project.pbxproj'); + + if (iosProjectFile.existsSync()) { + String content = iosProjectFile.readAsStringSync(); + RegExp regExp = RegExp(r'PRODUCT_BUNDLE_IDENTIFIER = (.+);'); + Match? match = regExp.firstMatch(content); + if (match != null) { + appBundleId = match.group(1); + } + } + + // read applicationId = "com.hejtech.flutter_fastlane_tutorial" from android/app/build.gradle + // or fallback to com.example.app + String? appPackage = 'com.example.app'; + File androidBuildFile = File('android/app/build.gradle'); + + if (androidBuildFile.existsSync()) { + String content = androidBuildFile.readAsStringSync(); + RegExp regExp = RegExp(r'applicationId = "(.+)"'); + Match? match = regExp.firstMatch(content); + if (match != null) { + appPackage = match.group(1); + } + } + + Map files = { + 'android/fastlane/.env': ''' +# This file should be added to your .gitignore file +# It contains sensitive information that should not be versioned +# https://docs.fastlane.tools/best-practices/keys/#where-do-i-store-my-keys +FIREBASE_APP_ID= +APP_PACKAGE_NAME=$appPackage +# Optional: Override the path to your Firebase service account JSON file (defaults to root of your project) +# GOOGLE_SERVICE_ACCOUNT_JSON_PATH= +''', + 'android/fastlane/Appfile': ''' +package_name(ENV["APP_PACKAGE_NAME"]) # e.g. com.krausefx.app +''', + 'android/fastlane/Fastfile': ''' +import "../../Fastfile" + +default_platform(:android) + +platform :android do + + # Build flutter Android app + lane :build do |options| + verify_env(envs: [ + "APP_PACKAGE_NAME" + ]) + + # Verify 'firebase_app_distribution_service_account.json' file exists + unless File.exist?(google_service_account_json_path) + UI.user_error!("google_service_account.json file not found. Please add it to the root of the flutter project. See https://docs.fastlane.tools/actions/supply/") + end + + # Verify version number is correct + if !is_ci && (!options[:version_number]) + version_number = get_version_from_pubspec() + continue = UI.confirm("Deploying version #{version_number} (from pubspec.yaml) to Play Store. Continue?") + + unless continue + UI.user_error!("Aborted") + end + end + + build_flutter_app( + type: options[:type] || "appbundle", + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number], + store: "playstore" + ) + end + + # Release to Play Store using Fastlane Supply (https://docs.fastlane.tools/actions/supply/) + desc "Release to Play Store" + lane :release_play_store do |options| + begin + build( + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number] + ) + + supply( + track: 'internal', + # Uncomment this if getting error "Only releases with status draft may be created on draft app." + # release_status: 'draft', + aab: "../build/app/outputs/bundle/release/app-release.aab", + json_key: google_service_account_json_path, + skip_upload_apk: true, # Upload the aab instead of apk + skip_upload_metadata: true, + skip_upload_changelogs: true, + skip_upload_images: true, + skip_upload_screenshots: true + ) + end + end + + # Release to Play Store using Firebase App Distribution (https://docs.fastlane.tools/actions/firebase_app_distribution/) + desc "Release to Play Store using Firebase App Distribution" + lane :release_play_store_using_firebase do |options| + begin + build( + type: 'apk', + no_codesign: options[:no_codesign], + config_only: options[:config_only], + build_number: options[:build_number], + version_number: options[:version_number] + ) + + firebase_app_distribution( + app: ENV["FIREBASE_APP_ID"], + android_artifact_path: "#{root_path}/build/app/outputs/flutter-apk/app-release.apk", + service_credentials_file: google_service_account_json_path, + # Use the following to enable debug mode + debug: true + ) + end + end +end''', + 'android/fastlane/Pluginfile': ''' +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-firebase_app_distribution' +gem 'fastlane-plugin-get_new_build_number' +''', + 'android/Gemfile': ''' +source "https://rubygems.org" + +gem "fastlane" +# takes version from flutter (pubspec.yaml) +gem "fastlane-plugin-flutter_version", git: "https://github.com/tianhaoz95/fastlane-plugin-flutter-version" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) +''', + + /// IOS + 'ios/fastlane/.env': ''' +ASC_KEY_ID="" +ASC_ISSUER_ID="" +ASC_KEY_P8_BASE64="" +MATCH_PASSWORD="" +MATCH_GIT_BASIC_AUTHORIZATION="" +APP_BUNDLE_ID=$appBundleId +FIREBASE_APP_ID= +# Optional: Override the path to your Firebase service account JSON file (defaults to root of your project) +# GOOGLE_SERVICE_ACCOUNT_JSON_PATH= +''', + 'ios/fastlane/Appfile': ''' +app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"]) +apple_id(ENV["FASTLANE_APPLE_ID"]) + +itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"]) +team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"]) +''', + 'ios/fastlane/Fastfile': ''' +import "../../Fastfile" + +default_platform(:ios) + + +platform :ios do + # Authenticate with Apple Store + private_lane :authenticate_apple_store do + app_store_connect_api_key( + key_id: ENV["ASC_KEY_ID"], + issuer_id: ENV["ASC_ISSUER_ID"], + key_content: ENV["ASC_KEY_P8_BASE64"], + is_key_content_base64: true, + in_house: false + ) + end + + # Build iOS app + lane :build_ipa do |options| + authenticate_apple_store + + build_flutter_app( + type: "ipa", + no_codesign: options[:no_codesign] || false, + config_only: options[:config_only] || false, + build_number: options[:build_number] || get_build_number('appstore'), + version_number: options[:version_number] || get_version_from_pubspec(), + store: "appstore" + ) + end + + + desc "Release a new build to Apple Store" + lane :release_app_store do |options| + verify_env(envs: [ + "ASC_KEY_ID", + "ASC_ISSUER_ID", + "ASC_KEY_P8_BASE64", + "APP_BUNDLE_ID", + "MATCH_PASSWORD", + "MATCH_GIT_BASIC_AUTHORIZATION", + ]) + + authenticate_apple_store + + build_number = options.fetch(:build_number, get_build_number('appstore')) + version_number = options.fetch(:version_number, get_version_from_pubspec()) + + # Verify version number is correct + if !is_ci && (!options[:version_number]) + continue = UI.confirm("Deploying version #{version_number} (from pubspec.yaml) to App Store. Continue?") + + unless continue + UI.user_error!("Aborted") + end + end + + # Sync certificates and profiles using match + UI.message("Syncing certificates and profiles") + + if is_ci + UI.message("CI detected. Setting up CI environment") + setup_ci + end + + sync_code_signing( + type: "appstore", + readonly: is_ci, + ) + + build_ipa( + build_number: build_number, + version_number: version_number + ) + + build_app( + skip_build_archive: true, + archive_path: "../build/ios/archive/Runner.xcarchive", + ) + + # If GoogleService-Info.plist exists and Pods/FirebaseCrashlytics exists + # Upload symbols to Firebase Crashlytics + if File.file?("../ios/Runner/GoogleService-Info.plist") && File.directory?("../ios/Pods/FirebaseCrashlytics") + upload_symbols_to_crashlytics( + gsp_path: "../ios/Runner/GoogleService-Info.plist" + ) + end + + upload_to_testflight( + skip_waiting_for_build_processing: true + ) + end + + # This is a work in progress, requiring ad-hoc export method + # desc "Release to Play Store using Firebase App Distribution" + # lane :release_to_firebase do |options| + # begin + # build_ipa + + # firebase_app_distribution( + # app: ENV["FIREBASE_APP_ID"], + # service_credentials_file: google_service_account_json_path, + # ipa_path: "../build/ios/Runner.ipa" + # ) + # end + # end +end +''', + 'ios/fastlane/Matchfile': ''' +git_url("https://github.com/your/app-certificates-and-profiles.git") +storage_mode("git") +type("appstore") # The default type, can be: appstore, adhoc, enterprise or development + +app_identifier([ENV["APP_BUNDLE_ID"]]) +''', + + 'ios/fastlane/Pluginfile': ''' +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-firebase_app_distribution' +gem 'fastlane-plugin-get_new_build_number' +''', + 'ios/Gemfile': ''' +source "https://rubygems.org" + +gem "fastlane" +# takes version from flutter (pubspec.yaml) +gem "fastlane-plugin-flutter_version", git: "https://github.com/tianhaoz95/fastlane-plugin-flutter-version" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) +''', + + /// Generics + 'Fastfile': ''' +opt_out_usage + +# Have an easy way to get the root of the project +def root_path + Dir.pwd.sub(/.*\\Kfastlane/, '').sub(/.*\\Kandroid/, '').sub(/.*\\Kios/, '').sub(/.*\\K\\/\\//, '') +end + +# Have an easy way to run flutter tasks on the root of the project +lane :sh_on_root do |options| + command = options[:command] + sh("cd #{root_path} && #{command}") +end + +# Tasks to be reused on each platform flow +lane :fetch_dependencies do + sh_on_root(command: "flutter pub get --suppress-analytics") +end + +# Tasks to be reused on each platform flow +lane :build_autogenerated_code do + sh_on_root(command: "flutter pub run build_runner build --delete-conflicting-outputs") +end + +# Tasks to be reused on each platform flow +lane :lint do + sh_on_root(command: "flutter format --suppress-analytics --set-exit-if-changed -n lib/main.dart lib/src/ test/") +end + +lane :build_flutter_app do |options| + pubspec_version_number = get_version_from_pubspec() + + type = options[:type] + build_number = options[:build_number] || get_build_number(options[:store]) + version_number = options[:version_number] || pubspec_version_number + no_codesign = options[:no_codesign] || false + config_only = options[:config_only] || false + commit = last_git_commit + + command = "flutter build #{type} --release --no-pub --suppress-analytics" + command += " --build-number=#{build_number}" if build_number.to_s != "" + command += " --build-name=#{version_number}" if version_number.to_s != "" + command += " --no-codesign" if no_codesign + command += " --config-only" if config_only + + UI.message("Building #{type} - version: #{version_number} - build: #{build_number} - commit: #{commit[:abbreviated_commit_hash]}") + + fetch_dependencies + + # Check if build_runner exists in pubspec.yaml + # If it does, run the build_runner command + if File.exist?("#{root_path}/pubspec.yaml") + pubspec_content = File.read("#{root_path}/pubspec.yaml") + if pubspec_content.include?("build_runner:") + build_autogenerated_code + end + end + + sh_on_root(command: command) +end + +# Tasks to be reused on each platform flow +lane :test do |options| + sh_on_root(command: "flutter test --no-pub --coverage --suppress-analytics") +end + + +# Private lane to verify all environment variables are set +private_lane :verify_env do |options| + # array of ENVS to check + envs = options.fetch(:envs, []) + + envs.each do |env| + if ENV[env].nil? || ENV[env].empty? + UI.user_error!("ENV \\"#{env}\\" is not set. Please set it in your environment variables (e.g. ios/fastlane/.env)") + end + end +end + +# A helper method to get the path to the firebase service account json file +def google_service_account_json_path + root_path + '/' + (ENV["GOOGLE_SERVICE_ACCOUNT_JSON_PATH"] || 'google_service_account.json') +end + +# Build number is a unique identifier for each build that is uploaded to the app store. +# This method will get the latest build number from the app store and increment it by 1. +# Ensure authenticate_apple_store is called before this method +def get_build_number(store) + return get_new_build_number( + bundle_identifier: store == "appstore" ? ENV["APP_BUNDLE_ID"] : nil, + package_name: store == "playstore" ? ENV["APP_PACKAGE_NAME"] : nil, + google_play_json_key_path: google_service_account_json_path + ).to_s +end + + +def get_version_from_pubspec + require 'yaml' + + # Define the correct path to pubspec.yaml relative to the Fastlane directory + pubspec_path = File.expand_path("#{root_path}/pubspec.yaml") + + # Check if the file exists to avoid errors + unless File.exist?(pubspec_path) + UI.error("pubspec.yaml file not found at path: #{pubspec_path}") + return nil + end + + # Parse the pubspec.yaml file + pubspec_content = File.read(pubspec_path) + + # Use regex to find the version number line and extract both version number and build number + version_line = pubspec_content.match(/version:\\s*(\\d+\\.\\d+\\.\\d+)\\+(\\d+)/) + if version_line + version_number = version_line[1] + else + UI.error("Version number not found in pubspec.yaml") + return nil + end + + return version_number +end +''', + 'google_service_account.json': ''' +Follow this guide to create a service account JSON file: https://docs.fastlane.tools/actions/supply/#setup +''', + 'android/key.properties': ''' +storePassword= +keyPassword= +keyAlias=release +storeFile=../key.jks +''', + '''.github/workflows/deploy-with-fastlane.yaml''': r''' +name: Publish iOS and Android release + +on: + release: + types: [published] + +env: + FLUTTER_CHANNEL: "stable" + RUBY_VERSION: "3.2.2" + +jobs: + build_ios: + name: Build iOS + # You can upgrade to more powerful machines if you need to + # See https://docs.github.com/en/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners#about-macos-larger-runners + runs-on: macos-latest + # Depending on how long your build takes, you might want to increase the timeou + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + working-directory: 'ios' + + - name: Run Flutter tasks + uses: subosito/flutter-action@v2.16.0 + with: + # Remember to specify flutter version in pubspec.yaml under environment + # https://github.com/subosito/flutter-action?tab=readme-ov-file#use-version-from-pubspecyaml + flutter-version-file: 'pubspec.yaml' + channel: ${{ env.FLUTTER_CHANNEL }} + cache: true + + - uses: maierj/fastlane-action@v3.1.0 + with: + lane: 'release_app_store' + subdirectory: ios + options: '{ "version_number": "${{ github.ref_name }}" }' + env: + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} + ASC_KEY_P8_BASE64: ${{ secrets.ASC_KEY_P8_BASE64 }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + APP_BUNDLE_ID: ${{ secrets.APP_BUNDLE_ID }} + + notify_ios: + name: Send Slack Notification about iOS build + needs: [build_ios] + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Send Slack Notification about iOS build + uses: rtCamp/action-slack-notify@v2 + if: ${{ !cancelled() && (success() || failure()) && env.SLACK_LOGS_WEBHOOK_PRESENT == 'true' }} + env: + SLACK_LOGS_WEBHOOK_PRESENT: ${{ secrets.SLACK_LOGS_WEBHOOK && 'true' || 'false' }} + SLACK_WEBHOOK: ${{ secrets.SLACK_LOGS_WEBHOOK }} + SLACK_CHANNEL: logs + SLACK_USERNAME: "${{ github.repository_owner }}" + SLACK_ICON: "https://github.com/${{ github.repository_owner }}.png?size=250" + SLACK_COLOR: "${{ contains(needs.*.result, 'success') && 'good' || 'danger' }}" + SLACK_TITLE: "${{ contains(needs.*.result, 'success') && 'Successfully released' || 'Error during release of' }} ${{ github.ref_name }} for iOS to TestFlight" + SLACK_FOOTER: "DevOps" + SLACK_MESSAGE: "${{ contains(needs.*.result, 'success') && 'Released:' || 'Release failed:' }} ${{github.event.head_commit.message}}" + + build_android: + name: Build Android + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + working-directory: 'android' + + - name: Run Flutter tasks + uses: subosito/flutter-action@v2.16.0 + with: + flutter-version-file: 'pubspec.yaml' + channel: ${{ env.FLUTTER_CHANNEL }} + cache: true + + - name: Create google_service_account.json + run: | + echo "${{ secrets.FIREBASE_SERVICE_ACCOUNT_BASE64 }}" | base64 --decode > google_service_account.json + + - name: Create key.jks + run: | + echo "${{ secrets.ANDROID_KEYSTORE_FILE_BASE64 }}" | base64 --decode > android/key.jks + + - name: Create key.properties + run: | + cat < android/key.properties + storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyPassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyAlias=release + storeFile=../key.jks + EOF + env: + ANDROID_KEY_STORE_PASSWORD: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + + - uses: maierj/fastlane-action@v3.1.0 + with: + lane: 'release_play_store' + subdirectory: android + options: '{ "version_number": "${{ github.ref_name }}" }' + env: + APP_PACKAGE_NAME: ${{ secrets.APP_PACKAGE_NAME }} + + notify_android: + name: Send Slack Notification about Android build + needs: [build_android] + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Send Slack Notification about Android build + uses: rtCamp/action-slack-notify@v2 + if: ${{ !cancelled() && (success() || failure()) && env.SLACK_LOGS_WEBHOOK_PRESENT == 'true' }} + env: + SLACK_LOGS_WEBHOOK_PRESENT: ${{ secrets.SLACK_LOGS_WEBHOOK && 'true' || 'false' }} + SLACK_WEBHOOK: ${{ secrets.SLACK_LOGS_WEBHOOK }} + SLACK_CHANNEL: logs + SLACK_USERNAME: "${{ github.repository_owner }}" + SLACK_ICON: "https://github.com/${{ github.repository_owner }}.png?size=250" + SLACK_COLOR: "${{ contains(needs.*.result, 'success') && 'good' || 'danger' }}" + SLACK_TITLE: "${{ contains(needs.*.result, 'success') && 'Successfully released' || 'Error during release of' }} ${{ github.ref_name }} for Android to Play Store" + SLACK_FOOTER: "DevOps" + SLACK_MESSAGE: "${{ contains(needs.*.result, 'success') && 'Released:' || 'Release failed:' }} ${{github.event.head_commit.message}}" +''', + }; + + // verify if the files already exist + List existingFiles = []; + + files.forEach((path, content) { + if (File.fromUri(Uri.file(path)).existsSync()) { + existingFiles.add(path); + } + }); + + bool override = false; + + // ask if he wish to override the file + if (existingFiles.isNotEmpty) { + print('The following files already exist:'); + existingFiles.forEach((file) { + print(file); + }); + stdout.write('Do you wish to override the files? (y/n): '); + String? answer = stdin.readLineSync(); + + override = answer?.toLowerCase() == 'y'; + } + + files.forEach((path, content) { + // if it exists and the user doesn't want to override, skip + if (File.fromUri(Uri.file(path)).existsSync() && !override) { + return; + } + + File file = File(path); + file.createSync(recursive: true); + file.writeAsStringSync(content); + print('File $path created successfully.'); + }); + + // Check if .gitignore contains '*.jks*, key.properties, google_service_account.json' + // and if not, append them + + File gitignore = File('.gitignore'); + if (gitignore.existsSync()) { + String content = gitignore.readAsStringSync(); + if (!content.contains('.env')) { + gitignore.writeAsStringSync('.env\n', mode: FileMode.append); + } + if (!content.contains('*.jks')) { + gitignore.writeAsStringSync('*.jks\n', mode: FileMode.append); + } + if (!content.contains('key.properties')) { + gitignore.writeAsStringSync('key.properties\n', mode: FileMode.append); + } + if (!content.contains('google_service_account.json')) { + gitignore.writeAsStringSync('google_service_account.json\n', + mode: FileMode.append); + } + + if (!content.contains('**/fastlane/report.xml')) { + gitignore.writeAsStringSync('**/fastlane/report.xml\n', + mode: FileMode.append); + } + + if (!content.contains('**/fastlane/Preview.html')) { + gitignore.writeAsStringSync('**/fastlane/Preview.html\n', + mode: FileMode.append); + } + + if (!content.contains('**/fastlane/screenshots')) { + gitignore.writeAsStringSync('**/fastlane/screenshots\n', + mode: FileMode.append); + } + + if (!content.contains('**/fastlane/test_output')) { + gitignore.writeAsStringSync('**/fastlane/test_output\n', + mode: FileMode.append); + } + } + + // Modify `android/app/build.gradle` and ensure it contains the following, + // ```gradle + // def keystoreProperties = new Properties() + // def keystorePropertiesFile = rootProject.file('key.properties') + // if (keystorePropertiesFile.exists()) { + // keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + // } + // ``` + + File androidBuildGradle = File('android/app/build.gradle'); + + if (androidBuildGradle.existsSync()) { + // Ask if user wants to automatically add the required configurations + + print( + 'Do you want to automatically add the required configurations to android/app/build.gradle? (y/n): '); + + String? answer = stdin.readLineSync(); + + if (answer?.toLowerCase() == 'y') { + // Read the content of the file + String content = androidBuildGradle.readAsStringSync(); + if (!content.contains('def keystoreProperties = new Properties()')) { + print('Adding keystoreProperties to android/app/build.gradle'); + // add it above the `android {` block + content = content.replaceFirst('android {', ''' +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + +android {'''); + } + + // If it does not contain signingConfigs { + // then add it above the buildTypes block + if (!content.contains('signingConfigs {')) { + print('Adding signingConfigs to android/app/build.gradle'); + content = content.replaceFirst('buildTypes {', ''' +signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + + buildTypes {'''); + } + + // Set signingConfig signingConfigs.release + print('Ensuring signingConfig is set to signingConfigs.release'); + content = content.replaceFirst( + RegExp(r'buildTypes \{[\s\S]*?release \{[\s\S]*?\}[\s\S]*?\}'), ''' +buildTypes { + release { + signingConfig signingConfigs.release + } + }'''); + + // Write the content back to the file + androidBuildGradle.writeAsStringSync(content); + } + } + + print('Fastlane setup completed successfully.'); +} diff --git a/beakpeek/ios/Gemfile b/beakpeek/ios/Gemfile new file mode 100644 index 00000000..84a4b3cf --- /dev/null +++ b/beakpeek/ios/Gemfile @@ -0,0 +1,8 @@ +source "https://rubygems.org" + +gem "fastlane" +# takes version from flutter (pubspec.yaml) +gem "fastlane-plugin-flutter_version", git: "https://github.com/tianhaoz95/fastlane-plugin-flutter-version" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/beakpeek/ios/fastlane/Appfile b/beakpeek/ios/fastlane/Appfile new file mode 100644 index 00000000..4a9c97a0 --- /dev/null +++ b/beakpeek/ios/fastlane/Appfile @@ -0,0 +1,5 @@ +app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"]) +apple_id(ENV["FASTLANE_APPLE_ID"]) + +itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"]) +team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"]) diff --git a/beakpeek/ios/fastlane/Fastfile b/beakpeek/ios/fastlane/Fastfile new file mode 100644 index 00000000..b7de472d --- /dev/null +++ b/beakpeek/ios/fastlane/Fastfile @@ -0,0 +1,107 @@ +import "../../Fastfile" + +default_platform(:ios) + + +platform :ios do + # Authenticate with Apple Store + private_lane :authenticate_apple_store do + app_store_connect_api_key( + key_id: ENV["ASC_KEY_ID"], + issuer_id: ENV["ASC_ISSUER_ID"], + key_content: ENV["ASC_KEY_P8_BASE64"], + is_key_content_base64: true, + in_house: false + ) + end + + # Build iOS app + lane :build_ipa do |options| + authenticate_apple_store + + build_flutter_app( + type: "ipa", + no_codesign: options[:no_codesign] || false, + config_only: options[:config_only] || false, + build_number: options[:build_number] || get_build_number('appstore'), + version_number: options[:version_number] || get_version_from_pubspec(), + store: "appstore" + ) + end + + + desc "Release a new build to Apple Store" + lane :release_app_store do |options| + verify_env(envs: [ + "ASC_KEY_ID", + "ASC_ISSUER_ID", + "ASC_KEY_P8_BASE64", + "APP_BUNDLE_ID", + "MATCH_PASSWORD", + "MATCH_GIT_BASIC_AUTHORIZATION", + ]) + + authenticate_apple_store + + build_number = options.fetch(:build_number, get_build_number('appstore')) + version_number = options.fetch(:version_number, get_version_from_pubspec()) + + # Verify version number is correct + if !is_ci && (!options[:version_number]) + continue = UI.confirm("Deploying version #{version_number} (from pubspec.yaml) to App Store. Continue?") + + unless continue + UI.user_error!("Aborted") + end + end + + # Sync certificates and profiles using match + UI.message("Syncing certificates and profiles") + + if is_ci + UI.message("CI detected. Setting up CI environment") + setup_ci + end + + sync_code_signing( + type: "appstore", + readonly: is_ci, + ) + + build_ipa( + build_number: build_number, + version_number: version_number + ) + + build_app( + skip_build_archive: true, + archive_path: "../build/ios/archive/Runner.xcarchive", + ) + + # If GoogleService-Info.plist exists and Pods/FirebaseCrashlytics exists + # Upload symbols to Firebase Crashlytics + if File.file?("../ios/Runner/GoogleService-Info.plist") && File.directory?("../ios/Pods/FirebaseCrashlytics") + upload_symbols_to_crashlytics( + gsp_path: "../ios/Runner/GoogleService-Info.plist" + ) + end + + upload_to_testflight( + skip_waiting_for_build_processing: true + ) + end + + # This is a work in progress, requiring ad-hoc export method + # desc "Release to Play Store using Firebase App Distribution" + # lane :release_to_firebase do |options| + # begin + # build_ipa + + # firebase_app_distribution( + # app: ENV["FIREBASE_APP_ID"], + # service_credentials_file: google_service_account_json_path, + # ipa_path: "../build/ios/Runner.ipa" + # ) + # end + # end +end diff --git a/beakpeek/ios/fastlane/Matchfile b/beakpeek/ios/fastlane/Matchfile new file mode 100644 index 00000000..99ffa403 --- /dev/null +++ b/beakpeek/ios/fastlane/Matchfile @@ -0,0 +1,5 @@ +git_url("https://github.com/your/app-certificates-and-profiles.git") +storage_mode("git") +type("appstore") # The default type, can be: appstore, adhoc, enterprise or development + +app_identifier([ENV["APP_BUNDLE_ID"]]) diff --git a/beakpeek/ios/fastlane/Pluginfile b/beakpeek/ios/fastlane/Pluginfile new file mode 100644 index 00000000..4a109fc2 --- /dev/null +++ b/beakpeek/ios/fastlane/Pluginfile @@ -0,0 +1,6 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-firebase_app_distribution' +gem 'fastlane-plugin-get_new_build_number' diff --git a/beakpeek/linux/.gitignore b/beakpeek/linux/.gitignore deleted file mode 100644 index d3896c98..00000000 --- a/beakpeek/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/beakpeek/linux/CMakeLists.txt b/beakpeek/linux/CMakeLists.txt deleted file mode 100644 index 7732db69..00000000 --- a/beakpeek/linux/CMakeLists.txt +++ /dev/null @@ -1,145 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "beakpeek") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.millennium.beakpeek") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Copy the native assets provided by the build.dart from all packages. -set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") -install(DIRECTORY "${NATIVE_ASSETS_DIR}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/beakpeek/linux/flutter/CMakeLists.txt b/beakpeek/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd0164..00000000 --- a/beakpeek/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/beakpeek/linux/flutter/generated_plugin_registrant.cc b/beakpeek/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 25f9f58f..00000000 --- a/beakpeek/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); - g_autoptr(FlPluginRegistrar) window_to_front_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin"); - window_to_front_plugin_register_with_registrar(window_to_front_registrar); -} diff --git a/beakpeek/linux/flutter/generated_plugin_registrant.h b/beakpeek/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/beakpeek/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/beakpeek/linux/flutter/generated_plugins.cmake b/beakpeek/linux/flutter/generated_plugins.cmake deleted file mode 100644 index e9e5df37..00000000 --- a/beakpeek/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - url_launcher_linux - window_to_front -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/beakpeek/linux/main.cc b/beakpeek/linux/main.cc deleted file mode 100644 index e7c5c543..00000000 --- a/beakpeek/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/beakpeek/linux/my_application.cc b/beakpeek/linux/my_application.cc deleted file mode 100644 index bbceaeb8..00000000 --- a/beakpeek/linux/my_application.cc +++ /dev/null @@ -1,124 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "beakpeek"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "beakpeek"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GApplication::startup. -static void my_application_startup(GApplication* application) { - //MyApplication* self = MY_APPLICATION(object); - - // Perform any actions required at application startup. - - G_APPLICATION_CLASS(my_application_parent_class)->startup(application); -} - -// Implements GApplication::shutdown. -static void my_application_shutdown(GApplication* application) { - //MyApplication* self = MY_APPLICATION(object); - - // Perform any actions required at application shutdown. - - G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_APPLICATION_CLASS(klass)->startup = my_application_startup; - G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/beakpeek/linux/my_application.h b/beakpeek/linux/my_application.h deleted file mode 100644 index 72271d5e..00000000 --- a/beakpeek/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/beakpeek/macos/.gitignore b/beakpeek/macos/.gitignore deleted file mode 100644 index 746adbb6..00000000 --- a/beakpeek/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/beakpeek/macos/Flutter/Flutter-Debug.xcconfig b/beakpeek/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b2..00000000 --- a/beakpeek/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/beakpeek/macos/Flutter/Flutter-Release.xcconfig b/beakpeek/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d15..00000000 --- a/beakpeek/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/beakpeek/macos/Flutter/GeneratedPluginRegistrant.swift b/beakpeek/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index f85c7930..00000000 --- a/beakpeek/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import device_info_plus -import flutter_web_auth_2 -import geolocator_apple -import path_provider_foundation -import sqflite -import url_launcher_macos -import window_to_front - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin")) - GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin")) -} diff --git a/beakpeek/macos/Podfile b/beakpeek/macos/Podfile deleted file mode 100644 index c795730d..00000000 --- a/beakpeek/macos/Podfile +++ /dev/null @@ -1,43 +0,0 @@ -platform :osx, '10.14' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/beakpeek/macos/Runner.xcodeproj/project.pbxproj b/beakpeek/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 21c24afd..00000000 --- a/beakpeek/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,705 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC10EC2044A3C60003C045; - remoteInfo = Runner; - }; - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* beakpeek.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "beakpeek.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 331C80D2294CF70F00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C80D6294CF71000263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C80D7294CF71000263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 331C80D6294CF71000263BE5 /* RunnerTests */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* beakpeek.app */, - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C80D4294CF70F00263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C80D1294CF70F00263BE5 /* Sources */, - 331C80D2294CF70F00263BE5 /* Frameworks */, - 331C80D3294CF70F00263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C80DA294CF71000263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* beakpeek.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C80D4294CF70F00263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 33CC10EC2044A3C60003C045; - }; - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 331C80D4294CF70F00263BE5 /* RunnerTests */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C80D3294CF70F00263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C80D1294CF70F00263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC10EC2044A3C60003C045 /* Runner */; - targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; - }; - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 331C80DB294CF71000263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.millennium.beakpeek.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beakpeek.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beakpeek"; - }; - name = Debug; - }; - 331C80DC294CF71000263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.millennium.beakpeek.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beakpeek.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beakpeek"; - }; - name = Release; - }; - 331C80DD294CF71000263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.millennium.beakpeek.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beakpeek.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beakpeek"; - }; - name = Profile; - }; - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C80DB294CF71000263BE5 /* Debug */, - 331C80DC294CF71000263BE5 /* Release */, - 331C80DD294CF71000263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/beakpeek/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/beakpeek/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/beakpeek/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/beakpeek/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/beakpeek/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 6ed807e5..00000000 --- a/beakpeek/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/beakpeek/macos/Runner.xcworkspace/contents.xcworkspacedata b/beakpeek/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/beakpeek/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/beakpeek/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/beakpeek/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/beakpeek/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/beakpeek/macos/Runner/AppDelegate.swift b/beakpeek/macos/Runner/AppDelegate.swift deleted file mode 100644 index 8e02df28..00000000 --- a/beakpeek/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@main -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f1..00000000 --- a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa4..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bdb57226..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index f083318e..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index 326c0e72..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cf..00000000 Binary files a/beakpeek/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/beakpeek/macos/Runner/Base.lproj/MainMenu.xib b/beakpeek/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 80e867a4..00000000 --- a/beakpeek/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/beakpeek/macos/Runner/Configs/AppInfo.xcconfig b/beakpeek/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index f48958c4..00000000 --- a/beakpeek/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = beakpeek - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.millennium.beakpeek - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright ยฉ 2024 com.millennium. All rights reserved. diff --git a/beakpeek/macos/Runner/Configs/Debug.xcconfig b/beakpeek/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd94..00000000 --- a/beakpeek/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/beakpeek/macos/Runner/Configs/Release.xcconfig b/beakpeek/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f495..00000000 --- a/beakpeek/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/beakpeek/macos/Runner/Configs/Warnings.xcconfig b/beakpeek/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf47..00000000 --- a/beakpeek/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/beakpeek/macos/Runner/DebugProfile.entitlements b/beakpeek/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30..00000000 --- a/beakpeek/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/beakpeek/macos/Runner/Info.plist b/beakpeek/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6..00000000 --- a/beakpeek/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/beakpeek/macos/Runner/MainFlutterWindow.swift b/beakpeek/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 3cc05eb2..00000000 --- a/beakpeek/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/beakpeek/macos/Runner/Release.entitlements b/beakpeek/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4..00000000 --- a/beakpeek/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/beakpeek/macos/RunnerTests/RunnerTests.swift b/beakpeek/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index 61f3bd1f..00000000 --- a/beakpeek/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Cocoa -import FlutterMacOS -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/beakpeek/pubspec.lock b/beakpeek/pubspec.lock index f5604f84..5c6a9064 100644 --- a/beakpeek/pubspec.lock +++ b/beakpeek/pubspec.lock @@ -1408,4 +1408,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.4.1 <4.0.0" - flutter: ">=3.22.0" + flutter: ">=3.24.2" diff --git a/beakpeek/pubspec.yaml b/beakpeek/pubspec.yaml index 03441a51..c47661d3 100644 --- a/beakpeek/pubspec.yaml +++ b/beakpeek/pubspec.yaml @@ -16,10 +16,11 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.3.8+1 +version: 4.0.0+8 environment: sdk: ">=3.4.1 <4.0.0" + flutter: 3.24.2 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..21fad01b --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,5 @@ +*.aux +*.fdb_latexmk +*.fls +*.gz +*.toc diff --git a/doc/Awards/Algorithms/algorithms.tex b/doc/Awards/Algorithms/algorithms.tex new file mode 100644 index 00000000..ced4d74d --- /dev/null +++ b/doc/Awards/Algorithms/algorithms.tex @@ -0,0 +1,185 @@ +\documentclass{article} + +\usepackage{listings} + +\title{ + +\\ +{Algorithms} +} + +\author{Millenium} + +\begin{document} + +\tableofcontents + +\newpage + +\section{Explanation of Algorithms in BirdMap} + +The BirdMap feature utilizes several key algorithms that enhance both performance and user experience. These algorithms ensure that the map is interactive, efficient, and responsive, making the app user-friendly and scalable. + +\subsection{Geolocation and Dynamic Camera Adjustment} +The \texttt{\_getCurrentLocation} function leverages the \textit{Geolocator} package to obtain the userโ€™s real-time location. The algorithm first checks if location services are enabled and if the necessary permissions are granted. Once a valid location within the boundaries of South Africa is obtained, the camera on the map is dynamically adjusted to center on the userโ€™s location. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency:} By updating the map's camera position based on the userโ€™s current location, the feature provides real-time relevance without unnecessary user input. + \item \textbf{User-Centric Design:} Automatically centering the map improves navigation and location-based features, enhancing the overall user experience. +\end{itemize} + +\subsection{Dynamic KML Parsing for Polygon Data} +The \texttt{\_loadKmlData} function loads KML data corresponding to the selected province. This data is parsed into polygon shapes using a custom \textit{KmlParser}, and the resulting polygons are then drawn on the map. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Scalability:} The algorithm dynamically loads KML data for specific provinces, avoiding the need to load unnecessary data, which improves memory usage and performance. + \item \textbf{Modularity:} By separating the parsing logic from the map itself, the design remains flexible and allows for the reuse of this functionality in other regions or datasets. +\end{itemize} + +\subsection{Filtering by Province and Month} +Users can filter the mapโ€™s content based on province and month. When a new province is selected, the camera is adjusted to focus on that province, and the corresponding KML data is reloaded. The month filter allows for further refinement of bird data shown on the map. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Interactivity:} The filter system allows users to explore different regions and times with ease, keeping the interface intuitive and engaging. + \item \textbf{Performance:} The dynamic reloading of only the necessary map data (polygons) helps in reducing the computational load and enhances the speed of the application. +\end{itemize} + +\subsection{Interactive Polygon Tapping} +The \texttt{\_onPolygonTapped} function is triggered when users tap on any polygon on the map. This action opens a modal sheet with detailed bird data for the selected region. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{User Engagement:} The ability to interact with specific map regions encourages users to explore the data, creating a more immersive experience. + \item \textbf{Clean User Interface:} By using modal sheets, the app avoids cluttering the map with too much information, providing details on demand. +\end{itemize} + +\subsection{Overall Design} +These algorithms work together to optimize both the performance and usability of the BirdMap feature. By loading data dynamically and offering interactive filters, the system is able to present relevant information to users while minimizing unnecessary resource usage. The modular and scalable design allows for easy future expansion and adaptability to other datasets or regions. + +\section{Explanation of Algorithms in the HeatMap} + +The HeatMap feature incorporates several effective algorithms to ensure efficient display and interactivity for bird population data, with dynamic filtering by month and species. Here's an overview of the key components and why they work so well. + +\subsection{Dynamic Pentad Data Loading} +The \texttt{loadPentadData} function dynamically loads bird sighting data. It retrieves population information for different species, computes polygons for regions (called pentads), and colors these polygons based on the reporting rate, i.e., how often birds are sighted in a particular region. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Dynamic Data Handling:} The map only loads the data it needs when required, keeping the app fast and responsive. It processes and adds polygons in batches, ensuring that the user experience remains smooth even with large datasets. + \item \textbf{Modularity:} The function that retrieves bird data is separate from the map rendering logic. This modular design makes the system more flexible and reusable in other parts of the app. +\end{itemize} + +\subsection{Color Palette and Visualization} +The \texttt{getColorForReportingRate} function assigns colors to the polygons based on the percentage of bird sightings (called the reporting rate). The color scale ranges from low to high, helping users quickly identify areas with high bird activity. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Visual Clarity:} The color-coded map makes it easy to understand bird distribution patterns at a glance. It turns raw data into meaningful visual insights. + \item \textbf{User Engagement:} Users can customize the color palette through the \texttt{PaletteSelector}, allowing them to personalize their experience and making the app more interactive. +\end{itemize} + +\subsection{Interactive Map Filtering} +The map allows users to filter the displayed data by month. By selecting a month, users can see how bird populations change throughout the year. This is managed via a dropdown menu that updates the polygons and the displayed data accordingly. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Real-time Updates:} The polygons are cleared and redrawn every time the user changes the month, ensuring the map always reflects the latest data. + \item \textbf{User Flexibility:} Users can easily explore different time periods, providing a more engaging and informative experience. +\end{itemize} + +\subsection{Efficient Map Rendering} +The Google Map widget efficiently renders polygons on the map. The polygons represent pentads and are generated dynamically based on bird sighting data. To maintain performance, the polygons are processed and added to the map in small batches. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency:} By only rendering relevant polygons and doing so in small increments, the map stays responsive and avoids performance bottlenecks. + \item \textbf{Interactivity:} The map allows for zooming and panning, ensuring users can explore the data interactively without any lag or slowdowns. +\end{itemize} + +\subsection{Endangered Species Alerts} +The app includes a dynamic alert that informs users if the bird species they are viewing is endangered. The app checks the bird's population size and displays an appropriate message, enhancing the user's awareness of conservation efforts. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Dynamic Warnings:} Users are alerted about endangered species, adding a layer of education to the app and increasing awareness of conservation issues. + \item \textbf{Seamless Integration:} These warnings are displayed alongside the map in a way that feels natural, without interrupting the user's experience. +\end{itemize} + +\subsection{Overall Design} +These algorithms ensure that the HeatMap feature is not only efficient but also interactive and user-friendly. By dynamically loading data, using intuitive visual cues, and providing interactive filtering, the app offers an engaging experience that adapts to the user's needs. + +\section{Explanation of Algorithms in LifeList} +The LifeList's algorithms are geared towards performance ensuring that the user always has access to the data with minimal delays, ensuring that it the App remains efficient and responsive.The SQLite database that is used has also been designed to be scalable whilst still maintaining efficiency. + +\subsection{SQLite Tables} +The LifeList database is comprised of 3 tables Birds, allBirds and Provinces + +\subsubsection{AllBirds Table} +The allBirds table stores all the data for the 800+ birds in South Africa.Images are also store as a Blob of base64. The user also has the ability to request for this table to be updated giving the latest data when they require it. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Offline access:} By keeping this table the user is able to access all information about all the birds in South Africa at anytime regardless of internet access. + \item \textbf{Better response time:} Having no need to constantly make request to the online API means that the information is always readily available and able to be displayed. + \item \textbf{Lower storage requirements:} By storing images as a string of base64 we drastically decease the size of the database + \item \textbf{Redundancy:} All birds also stores the online URL for the images this means that should the image fail to load the online image is retrieved and stored in the table for next usage. +\end{itemize} + +\subsubsection{Bird Table } +The \texttt{Bird} table stores rudimentary bird data for birds that have been seen. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Saves storage:} By keeping this table small we minimize storage requirements. + \item \textbf{Seamless Integration:} As the \texttt{Bird} stores minimal information it make it easier to upload to the users online account allowing for the transfer of data to be quicker and more cost effective. +\end{itemize} + +\subsubsection{Provinces Table } +The \texttt{Provinces} table stores the Bird is and a list of provinces that the bird is found in. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This table allows for 1 query to be made to find number of birds in a province and which birds are found in which provinces. + \item \textbf{Storage Efficiency:} This allows the \texttt{AllBirds} table to be made smaller and faster. +\end{itemize} + +\subsubsection{Online Profile Update} +The\texttt{\ fetchUserLifelistString} retrieves the necessary data from the \texttt{\ Birds} table that is need for online profile storage. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This allows for efficient retrieval of the life list to be stored online . + \item \textbf{Storage Efficiency:} This allows the online storage to be smaller and more manageable. +\end{itemize} + +\subsubsection{Duplicate inserting} +The\texttt{\ isDuplicate} checks if the bird to be inserted is in the life list already. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This prevents duplicate data from even being attempted to be inserted and it allows the front end to identify if a bird is in the Life List without being queried. +\end{itemize} + +\subsubsection{Bird Provinces} +The\texttt{\ getBirdProvinces} return all the provices that a bird is in. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} Allow for 1 query that retrieves all provinces that a bird is seen in. +\end{itemize} + +\subsubsection{Achievement Progress} +The\texttt{\ precentLifeListBirds} returns the percentage of the current achievement. By querying \texttt{\ Provinces} and \texttt{\ Birds} tables we are able to calculate the percentage of the achievements.This query is called upon inserting a bird into life list and the value is stored in the user model. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} Allow for 1 query that retrieves the current percentage. + \item \textbf{Seamless Integration :} By storing this data in the user model it is always available when need so loading time is minimized. +\end{itemize} + +\subsubsection{Overall Design} +These tables and algorithms ensure that the LifeList as well as all bird information is available at all times. By loading the LifeList as the app starts we ensure that the data is always readily available to the user anytime the user needs it. These algorithms are also geared to minimize loading times and save storage space by breaking the data down into smaller and more manageable tables. +\end{document} diff --git a/doc/Awards/Datascience/datascience.tex b/doc/Awards/Datascience/datascience.tex new file mode 100644 index 00000000..e6b3407f --- /dev/null +++ b/doc/Awards/Datascience/datascience.tex @@ -0,0 +1,177 @@ +\documentclass{article} + +\usepackage{listings} + +\title{ + +\\ +{Installation Manual} +} + +\author{Millenium} + +\begin{document} + +\tableofcontents + +\newpage + +\section{Algorithms} +\subsection{Data Science behind the HeatMap} + +The HeatMap feature processes and visualizes a vast dataset, encompassing over \textbf{16,673 pentads} (geographic regions) and more than \textbf{1.4 million bird entries}. To manage such large-scale data, the app utilizes various algorithms to optimize data handling, visualization, and performance. + +\subsubsection{Dynamic Pentad Data Loading} +Given the scale of the data, the \texttt{loadPentadData} function dynamically loads bird sighting data only as needed. Instead of attempting to load all 16,673 pentads and millions of bird entries simultaneously, the algorithm fetches data relevant to the user's selections, such as species and month, and updates the map accordingly. It parses the kml file and calculates the coordinates based on the data. The coordinates are then made into a polygon which is linked with the reporting rate of the bird. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Data Efficiency:} By fetching only necessary data based on user input, the app minimizes memory usage and processing time, allowing it to handle a database with over 1.4 million entries efficiently. + \item \textbf{Batch Processing:} The large dataset is processed in small increments (batches), preventing performance degradation and ensuring smooth interaction even with vast amounts of data. +\end{itemize} + +\subsubsection{Data Visualization with Color Coding} +With a database of this size, data visualization is key to transforming raw data into actionable insights. The \texttt{getColorForReportingRate} function assigns colors to polygons based on the reporting rate (i.e., bird sighting percentages in each pentad). This color coding makes it easier to interpret patterns across thousands of pentads. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Data-Driven Visualization:} Transforming over a million bird entries into an intuitive color-coded map allows users to quickly understand the distribution and frequency of sightings. + \item \textbf{Scalable Visualization:} As users filter the data, the map dynamically recolors the polygons to reflect updated sighting patterns, providing immediate visual feedback. +\end{itemize} + +\subsubsection{Interactive Map Filtering} +With 16,673 pentads in the dataset, filtering is essential for managing data. The app allows users to filter bird sighting data by month, significantly reducing the dataset to a manageable size while maintaining interactivity. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Time-Based Filtering:} Birds migrate seasonally, and sightings vary by month. The app allows users to focus on specific months, making the data more relevant and easier to interpret. + \item \textbf{Interactive Scalability:} Filtering by time and species reduces the complexity of rendering the data, allowing the system to handle large datasets efficiently and update the map in real-time. +\end{itemize} + +\subsubsection{Efficient Data Rendering} +Rendering polygons for 16,673 pentads and over a million bird entries is computationally intensive. The app uses the Google Map widget to render polygons dynamically, processing bird sighting data in small batches to avoid performance bottlenecks. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Real-Time Data Processing:} The app only processes and renders polygons relevant to the current view or filter, keeping the performance optimized. + \item \textbf{Performance Optimization:} By processing and rendering polygons in batches, the app remains responsive, even when dealing with large datasets. +\end{itemize} + +\subsubsection{Conclusion} +These algorithms enable the HeatMap feature to efficiently manage and visualize over 1.4 million bird sightings across 16,673 pentads. By dynamically loading, filtering, and rendering data, the app delivers a responsive and scalable user experience, leveraging data science to provide actionable insights on bird populations and conservation. + +\subsection{Data Science behind the BirdMap} + +The BirdMap feature handles a large dataset consisting of \textbf{16,673 pentads} (geographic regions) and over \textbf{1.4 million bird entries}. The following algorithms ensure efficient data management, visualization, and interactivity on the map. + +\subsubsection{Dynamic Location and KML Data Loading} +The \texttt{\_getCurrentLocation} function dynamically centers the map based on the user's current location, while the \texttt{\_loadKmlData} function loads KML data that represents polygon shapes for provinces. This approach allows the app to manage large datasets and visualize geographic information in real-time. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Data Efficiency:} By dynamically loading polygon data for specific provinces, the app minimizes the amount of data that needs to be processed, ensuring that only the relevant information is displayed. + \item \textbf{Scalable Processing:} Instead of attempting to load all 16,673 pentads or 1.4 million bird entries at once, the app fetches data on demand, optimizing performance and memory usage. +\end{itemize} + +\subsubsection{Data Filtering by Province and Month} +The map provides filtering options based on province and month, allowing users to narrow down the data to specific geographic areas and time frames. This significantly reduces the amount of data the app needs to process and display. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Interactive Data Reduction:} Filtering helps manage the large dataset by focusing on a specific province or month, making the data more relevant and easier to handle. + \item \textbf{Efficient Querying:} By querying the backend for bird sightings based on the selected filters, the app reduces the need to handle unnecessary data, improving both performance and responsiveness. +\end{itemize} + +\subsubsection{Polygon-Based Data Visualization} +The polygons representing each pentad are color-coded based on bird sighting reporting rates. This visual approach transforms the large dataset into an easy-to-understand map, where users can quickly identify areas with high or low bird sightings. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Visual Representation of Data:} Color-coded polygons provide an intuitive way to understand bird sighting distribution across thousands of pentads, transforming over 1.4 million entries into a simple visual format. + \item \textbf{Scalable Visualization:} As the user zooms in or filters the data, polygons are updated in real-time, ensuring the map stays relevant and responsive. +\end{itemize} + +\subsubsection{Real-Time Map Interaction} +The app leverages Google Maps for real-time interaction, allowing users to zoom, pan, and click on polygons to get more information about bird sightings in specific regions. Each polygon represents a pentad, and tapping on it provides detailed data about birds in that area. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Real-Time Data Updates:} Users can interact with the map to view specific bird data, with the polygons and information updating in real-time based on user actions. + \item \textbf{Efficient Event Handling:} The app only updates the relevant portions of the map (such as polygons) based on user input, maintaining high performance even with large datasets. +\end{itemize} + +\subsubsection{Conclusion} +The BirdMap feature efficiently handles over 1.4 million bird entries and 16,673 pentads using dynamic data loading, filtering, and polygon-based visualization. These algorithms ensure that the app remains responsive, interactive, and scalable, providing users with real-time insights into bird populations and sightings. + +\section{Explanation of Algorithms in LifeList} +The LifeList's algorithms are geared towards performance ensuring that the user always has access to the data with minimal delays, ensuring that it the App remains efficient and responsive.The SQLite database that is used has also been designed to be scalable whilst still maintaining efficiency. + +\subsection{SQLite Tables} +The LifeList database is comprised of 3 tables Birds, allBirds and Provinces + +\subsubsection{AllBirds Table} +The allBirds table stores all the data for the 800+ birds in South Africa.Images are also store as a Blob of base64. The user also has the ability to request for this table to be updated giving the latest data when they require it. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Offline access:} By keeping this table the user is able to access all information about all the birds in South Africa at anytime regardless of internet access. + \item \textbf{Better response time:} Having no need to constantly make request to the online API means that the information is always readily available and able to be displayed. + \item \textbf{Lower storage requirements:} By storing images as a string of base64 we drastically decease the size of the database + \item \textbf{Redundancy:} All birds also stores the online URL for the images this means that should the image fail to load the online image is retrieved and stored in the table for next usage. +\end{itemize} + +\subsubsection{Bird Table } +The \texttt{Bird} table stores rudimentary bird data for birds that have been seen. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Saves storage:} By keeping this table small we minimize storage requirements. + \item \textbf{Seamless Integration:} As the \texttt{Bird} stores minimal information it make it easier to upload to the users online account allowing for the transfer of data to be quicker and more cost effective. +\end{itemize} + +\subsubsection{Provinces Table } +The \texttt{Provinces} table stores the Bird is and a list of provinces that the bird is found in. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This table allows for 1 query to be made to find number of birds in a province and which birds are found in which provinces. + \item \textbf{Storage Efficiency:} This allows the \texttt{AllBirds} table to be made smaller and faster. +\end{itemize} + +\subsubsection{Online Profile Update} +The\texttt{\ fetchUserLifelistString} retrieves the necessary data from the \texttt{\ Birds} table that is need for online profile storage. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This allows for efficient retrieval of the life list to be stored online . + \item \textbf{Storage Efficiency:} This allows the online storage to be smaller and more manageable. +\end{itemize} + +\subsubsection{Duplicate inserting} +The\texttt{\ isDuplicate} checks if the bird to be inserted is in the life list already. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} This prevents duplicate data from even being attempted to be inserted and it allows the front end to identify if a bird is in the Life List without being queried. +\end{itemize} + +\subsubsection{Bird Provinces} +The\texttt{\ getBirdProvinces} return all the provices that a bird is in. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} Allow for 1 query that retrieves all provinces that a bird is seen in. +\end{itemize} + +\subsubsection{Achievement Progress} +The\texttt{\ precentLifeListBirds} returns the percentage of the current achievement. By querying \texttt{\ Provinces} and \texttt{\ Birds} tables we are able to calculate the percentage of the achievements.This query is called upon inserting a bird into life list and the value is stored in the user model. + +\textbf{Why it's effective:} +\begin{itemize} + \item \textbf{Efficiency :} Allow for 1 query that retrieves the current percentage. + \item \textbf{Seamless Integration :} By storing this data in the user model it is always available when need so loading time is minimized. +\end{itemize} + +\subsubsection{Overall Design} +These tables and algorithms ensure that the LifeList as well as all bird information is available at all times. By loading the LifeList as the app starts we ensure that the data is always readily available to the user anytime the user needs it. These algorithms are also geared to minimize loading times and save storage space by breaking the data down into smaller and more manageable tables. +\end{document} diff --git a/doc/Awards/DevOps/devops.tex b/doc/Awards/DevOps/devops.tex new file mode 100644 index 00000000..79006098 --- /dev/null +++ b/doc/Awards/DevOps/devops.tex @@ -0,0 +1,260 @@ +\documentclass{article} + +\usepackage{listings} + +\title{ + +\\ +{Installation Manual} +} + +\author{Millenium} + +\begin{document} + +\tableofcontents + +\newpage + + +\section{Introduction} + + +BeakPeek was developed with a backend and frontend and will there fore have two +seperate sub sections for installation for the frontend and backend installation + +\section{Prerequisites} + +\subsubsection{Frontend} + +\begin{itemize} + \item Flutter SDK + \item Android SDK Platform, API 35.0.1 + \item Android SDK Command-line Tools + \item Android SDK Build-Tools + \item Android SDK Platform-Tools + \item Android Emulator +\end{itemize} + +The full guide on how to install of of these can be found at the URL + + +\begin{itemize} + \item Select the operating system you are using + \item Select android +\end{itemize} + +\subsubsection{Backend} + +\begin{itemize} + \item .Net SDK 8.0 + \item ASP.Net Core Runtime 8.0 + \item .Net Runtime 8.0 + \item Docker Desktop 4.42 >= +\end{itemize} + +The full guide on how to install all of the .Net dependencies can be found at +the URL: + + +\begin{itemize} + \item Then select the operating system you are using from the choices available +\end{itemize} + +The full guide on how to install docker desktop can be found at the URL: + + +\begin{itemize} + \item Then select the operating system you are using from the choices available +\end{itemize} + +\section{Installation} + +To start with installing BeakPeek for local development first clone the +repository onto your local machine. + + +\begin{lstlisting} +git clone https://github.com/COS301-SE-2024/BeakPeek +\end{lstlisting} + +Then open the directory/folder that you cloned the repository into either with a +file explorer or preferably with the command line of your operating system + +\subsubsection{Frontend Installation} + +If you are only interested in using/working on the frontend of BeakPeek then you +can clone only the frontend directory of BeakPeek by running these commmands: + +\begin{lstlisting} +git clone -n --depth=1 --filter=tree:0 \ + https://github.com/COS301-SE-2024/BeakPeek +cd BeakPeek +git sparse-checkout set --no-cone beakpeek +git checkout +\end{lstlisting} + +\subsubsection{Backend Installation} + + +If you are only interested in using/working on the backend of BeakPeek then you +can clone only the frontend directory of BeakPeek by running the following to +clone the entire backend which includes the test + +\begin{lstlisting} +git clone -n --depth=1 --filter=tree:0 \ + https://github.com/COS301-SE-2024/BeakPeek +cd BeakPeek +git sparse-checkout set --no-cone dotnet +git checkout +\end{lstlisting} + +Or if you only want the Main Api + +\begin{lstlisting} +git clone -n --depth=1 --filter=tree:0 \ + https://github.com/COS301-SE-2024/BeakPeek +cd BeakPeek +git sparse-checkout set --no-cone dotnet/BeakPeekApi +git checkout +\end{lstlisting} + +\section{Running the code} + +\subsection{Running the Frontend} + +You will have to add a single file filled with your own details for connecting +to your own login provider, BeakPeek uses Azure so the following is used for the +Azure Config which just needs to be added to the lib directory in the beakpeek +directory. The file should be called azure_config.dart + +\begin{lstlisting} +String domain = 'com.millennium.beakpeek'; +String clientID = ''; +String issuer = + 'https://'; +String bundlerID = 'com.millennium.beakpeek'; +String redirectURL = 'com.millennium.beakpeek://login-callback'; +String discoveryURL = + 'https://'; +String scope = 'https://beakpeak.onmicrosoft.com/com.millennium.beakpeek/callback'; + +String initialUrl = ''; +String tokenUrl = + ''; + +String accessToken = ''; +bool loggedIN = false; +\end{lstlisting} + +To run the frontend you will have to make sure that there is an android phone +installed on the android emulator and that all of the dependencies are +installed. + +To ensure that all of the flutter dependencies are installed run the following +in the './beakpeek' directory using the command line: + +\begin{lstlisting} +flutter pub get +\end{lstlisting} + +To verify that you have a running emulator for running the fronend run the +following command: + +\begin{lstlisting} +flutter devices +\end{lstlisting} + +To run the app run the following command + +\begin{lstlisting} +flutter run +\end{lstlisting} + +\subsection{Running the Backend} + +You will have to add a single file to the './dotnet/BeakPeekApi' directory as +usually it contains api keys, to add it you can make a new file with the +following contents in it giving your own flickr api key where necessary + +\begin{lstlisting} +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "FLICKR_API_KEY": "enable enable aspnet-UserApi-e0155ff6-a75d-49c9-a66b-a0ed4e5df0d2 + true diff --git a/dotnet/UserApi/UserApi/packages.lock.json b/dotnet/UserApi/UserApi/packages.lock.json new file mode 100644 index 00000000..a16f9696 --- /dev/null +++ b/dotnet/UserApi/UserApi/packages.lock.json @@ -0,0 +1,1549 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Microsoft.AspNetCore.Authentication.Google": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "Hc6PfWcpNCSq9ZhesZSgLD62ACsxBuAx9jhkOv+No5tx+AdPPU+tonrxzM+PuTDvijOVPy4YydzuDK9NR4Z+PQ==" + }, + "Microsoft.AspNetCore.Authentication.JwtBearer": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "J145j2LgD4kkkNkrf5DW/pKzithZRKN5EFY+KAO3SqweMyDfv4cgKgtOIsv2bhrOLGqPJixuZkZte7LfK1seYQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2" + } + }, + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "y/2e8g+A2kRiyuHK+KWO8AgKZL6+heKM/Y2ijP4HVMP3QJFWK9xakXucMqC8A4hvL1U9JTEjOuZoa+c7Tr/XYA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.8" + } + }, + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "OtjXwZkWdW7UTQ1XR/IGWmeVCujnly0fjjCbUxsjHC1o3cB9iWQRTREdL4931Ulwk9RRgjYQ/mAo7k8ggcZ9xQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.8", + "Microsoft.Extensions.Identity.Stores": "8.0.8" + } + }, + "Microsoft.AspNetCore.Identity.UI": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "tvweO7FJ+4nPTYK/CXMyPtmT/0GVAOsoEKbzaXffI0NVKUS1lujWX9fTtEji+U+S7zjmtZSdqdQ+CqCN45cuBA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Embedded": "8.0.8", + "Microsoft.Extensions.Identity.Stores": "8.0.8" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "IDB7Xs16hN/3VkWFCCa4r3fqoJxMVezwq418gr8dBkRBO0pxH+BX/Kjk/U3PYXDvzVLkXqUgJsHv1XoFrJbZPQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.8", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "A2F52W+hnGqvprx37HcAnYnJv4QoFFdc9cxd/QGNSd1vCu1I0eAEKRd0r9KS3E5I5RRj/m9XJfYCyTdy1cdn5Q==", + "dependencies": { + "Microsoft.Data.SqlClient": "5.1.5", + "Microsoft.EntityFrameworkCore.Relational": "8.0.8" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Direct", + "requested": "[8.0.8, )", + "resolved": "8.0.8", + "contentHash": "wjDNbLJk86QpZt2JxJuNVzpBKIbEQsgcJYHGeIFTBuK6NEgvJvyxgneg059HfSJmTVdInZ61lTO4sJGCfFr7+w==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Design": "8.0.8" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Design": { + "type": "Direct", + "requested": "[8.0.5, )", + "resolved": "8.0.5", + "contentHash": "HvLrIbZDPXD4n6kyk44O1nnzpQ6lExP/2zwl/Hj+Bpefrvl/l7QcsZKZXG6w4iDSSzseTKcIsOKBiQRua2WPtw==", + "dependencies": { + "Microsoft.DotNet.Scaffolding.Shared": "8.0.5", + "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": "8.0.5" + } + }, + "Swashbukle.AspNetCore": { + "type": "Direct", + "requested": "[2.0.0, )", + "resolved": "2.0.0", + "contentHash": "g9bdxGyi9//kNLDmzqS+nlYZNDN8FmoPN3QVELXoC9A2QDX58/xx6qdZESiQ14uFLKgoU5pxPaWRtFhbUj4g5Q==", + "dependencies": { + "Swashbuckle.AspNetCore": "6.2.3" + } + }, + "Wangkanai.Detection": { + "type": "Direct", + "requested": "[8.14.0, )", + "resolved": "8.14.0", + "contentHash": "wwsSQrm0FFnlnxgsAUjWA5ersoQSQefUN1wVSAN2s5S3T5UDTkW6DJ5/75fYzjcheUWuKK8wxEdycSZ2EkdIfQ==", + "dependencies": { + "Microsoft.SourceLink.GitHub": "8.0.0", + "Wangkanai.Hosting": "3.2.0", + "Wangkanai.System": "5.0.0" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.35.0", + "contentHash": "hENcx03Jyuqv05F4RBEPbxz29UrM3Nbhnr6Wl6NQpoU9BCIbL3XLentrxDCTrH54NLS11Exxi/o8MYgT/cnKFA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "System.Diagnostics.DiagnosticSource": "6.0.1", + "System.Memory.Data": "1.0.2", + "System.Numerics.Vectors": "4.5.0", + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.10.3", + "contentHash": "l1Xm2MWOF2Mzcwuarlw8kWQXLZk3UeB55aQXVyjj23aBfDwOZ3gu5GP2kJ6KlmZeZv2TCzw7x4L3V36iNr3gww==", + "dependencies": { + "Azure.Core": "1.35.0", + "Microsoft.Identity.Client": "4.56.0", + "Microsoft.Identity.Client.Extensions.Msal": "4.56.0", + "System.Memory": "4.5.4", + "System.Security.Cryptography.ProtectedData": "4.7.0", + "System.Text.Json": "4.7.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Humanizer": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "/FUTD3cEceAAmJSCPN9+J+VhGwmL/C12jvwlyM1DFXShEMsBzvLzLqSrJ2rb+k/W2znKw7JyflZgZpyE+tI7lA==", + "dependencies": { + "Humanizer.Core.af": "2.14.1", + "Humanizer.Core.ar": "2.14.1", + "Humanizer.Core.az": "2.14.1", + "Humanizer.Core.bg": "2.14.1", + "Humanizer.Core.bn-BD": "2.14.1", + "Humanizer.Core.cs": "2.14.1", + "Humanizer.Core.da": "2.14.1", + "Humanizer.Core.de": "2.14.1", + "Humanizer.Core.el": "2.14.1", + "Humanizer.Core.es": "2.14.1", + "Humanizer.Core.fa": "2.14.1", + "Humanizer.Core.fi-FI": "2.14.1", + "Humanizer.Core.fr": "2.14.1", + "Humanizer.Core.fr-BE": "2.14.1", + "Humanizer.Core.he": "2.14.1", + "Humanizer.Core.hr": "2.14.1", + "Humanizer.Core.hu": "2.14.1", + "Humanizer.Core.hy": "2.14.1", + "Humanizer.Core.id": "2.14.1", + "Humanizer.Core.is": "2.14.1", + "Humanizer.Core.it": "2.14.1", + "Humanizer.Core.ja": "2.14.1", + "Humanizer.Core.ko-KR": "2.14.1", + "Humanizer.Core.ku": "2.14.1", + "Humanizer.Core.lv": "2.14.1", + "Humanizer.Core.ms-MY": "2.14.1", + "Humanizer.Core.mt": "2.14.1", + "Humanizer.Core.nb": "2.14.1", + "Humanizer.Core.nb-NO": "2.14.1", + "Humanizer.Core.nl": "2.14.1", + "Humanizer.Core.pl": "2.14.1", + "Humanizer.Core.pt": "2.14.1", + "Humanizer.Core.ro": "2.14.1", + "Humanizer.Core.ru": "2.14.1", + "Humanizer.Core.sk": "2.14.1", + "Humanizer.Core.sl": "2.14.1", + "Humanizer.Core.sr": "2.14.1", + "Humanizer.Core.sr-Latn": "2.14.1", + "Humanizer.Core.sv": "2.14.1", + "Humanizer.Core.th-TH": "2.14.1", + "Humanizer.Core.tr": "2.14.1", + "Humanizer.Core.uk": "2.14.1", + "Humanizer.Core.uz-Cyrl-UZ": "2.14.1", + "Humanizer.Core.uz-Latn-UZ": "2.14.1", + "Humanizer.Core.vi": "2.14.1", + "Humanizer.Core.zh-CN": "2.14.1", + "Humanizer.Core.zh-Hans": "2.14.1", + "Humanizer.Core.zh-Hant": "2.14.1" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Humanizer.Core.af": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "BoQHyu5le+xxKOw+/AUM7CLXneM/Bh3++0qh1u0+D95n6f9eGt9kNc8LcAHLIOwId7Sd5hiAaaav0Nimj3peNw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ar": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "3d1V10LDtmqg5bZjWkA/EkmGFeSfNBcyCH+TiHcHP+HGQQmRq3eBaLcLnOJbVQVn3Z6Ak8GOte4RX4kVCxQlFA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.az": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "8Z/tp9PdHr/K2Stve2Qs/7uqWPWLUK9D8sOZDNzyv42e20bSoJkHFn7SFoxhmaoVLJwku2jp6P7HuwrfkrP18Q==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.bg": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "S+hIEHicrOcbV2TBtyoPp1AVIGsBzlarOGThhQYCnP6QzEYo/5imtok6LMmhZeTnBFoKhM8yJqRfvJ5yqVQKSQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.bn-BD": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "U3bfj90tnUDRKlL1ZFlzhCHoVgpTcqUlTQxjvGCaFKb+734TTu3nkHUWVZltA1E/swTvimo/aXLtkxnLFrc0EQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.cs": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "jWrQkiCTy3L2u1T86cFkgijX6k7hoB0pdcFMWYaSZnm6rvG/XJE40tfhYyKhYYgIc1x9P2GO5AC7xXvFnFdqMQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.da": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "5o0rJyE/2wWUUphC79rgYDnif/21MKTTx9LIzRVz9cjCIVFrJ2bDyR2gapvI9D6fjoyvD1NAfkN18SHBsO8S9g==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.de": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "9JD/p+rqjb8f5RdZ3aEJqbjMYkbk4VFii2QDnnOdNo6ywEfg/A5YeOQ55CaBJmy7KvV4tOK4+qHJnX/tg3Z54A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.el": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "Xmv6sTL5mqjOWGGpqY7bvbfK5RngaUHSa8fYDGSLyxY9mGdNbDcasnRnMOvi0SxJS9gAqBCn21Xi90n2SHZbFA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.es": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "e//OIAeMB7pjBV1HqqI4pM2Bcw3Jwgpyz9G5Fi4c+RJvhqFwztoWxW57PzTnNJE2lbhGGLQZihFZjsbTUsbczA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.fa": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "nzDOj1x0NgjXMjsQxrET21t1FbdoRYujzbmZoR8u8ou5CBWY1UNca0j6n/PEJR/iUbt4IxstpszRy41wL/BrpA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.fi-FI": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "Vnxxx4LUhp3AzowYi6lZLAA9Lh8UqkdwRh4IE2qDXiVpbo08rSbokATaEzFS+o+/jCNZBmoyyyph3vgmcSzhhQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.fr": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "2p4g0BYNzFS3u9SOIDByp2VClYKO0K1ecDV4BkB9EYdEPWfFODYnF+8CH8LpUrpxL2TuWo2fiFx/4Jcmrnkbpg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.fr-BE": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "o6R3SerxCRn5Ij8nCihDNMGXlaJ/1AqefteAssgmU2qXYlSAGdhxmnrQAXZUDlE4YWt/XQ6VkNLtH7oMqsSPFQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.he": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "FPsAhy7Iw6hb+ZitLgYC26xNcgGAHXb0V823yFAzcyoL5ozM+DCJtYfDPYiOpsJhEZmKFTM9No0jUn1M89WGvg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.hr": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "chnaD89yOlST142AMkAKLuzRcV5df3yyhDyRU5rypDiqrq2HN8y1UR3h1IicEAEtXLoOEQyjSAkAQ6QuXkn7aw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.hu": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "hAfnaoF9LTGU/CmFdbnvugN4tIs8ppevVMe3e5bD24+tuKsggMc5hYta9aiydI8JH9JnuVmxvNI4DJee1tK05A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.hy": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "sVIKxOiSBUb4gStRHo9XwwAg9w7TNvAXbjy176gyTtaTiZkcjr9aCPziUlYAF07oNz6SdwdC2mwJBGgvZ0Sl2g==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.id": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "4Zl3GTvk3a49Ia/WDNQ97eCupjjQRs2iCIZEQdmkiqyaLWttfb+cYXDMGthP42nufUL0SRsvBctN67oSpnXtsg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.is": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "R67A9j/nNgcWzU7gZy1AJ07ABSLvogRbqOWvfRDn4q6hNdbg/mjGjZBp4qCTPnB2mHQQTCKo3oeCUayBCNIBCw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.it": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "jYxGeN4XIKHVND02FZ+Woir3CUTyBhLsqxu9iqR/9BISArkMf1Px6i5pRZnvq4fc5Zn1qw71GKKoCaHDJBsLFw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ja": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "TM3ablFNoYx4cYJybmRgpDioHpiKSD7q0QtMrmpsqwtiiEsdW5zz/q4PolwAczFnvrKpN6nBXdjnPPKVet93ng==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ko-KR": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "CtvwvK941k/U0r8PGdEuBEMdW6jv/rBiA9tUhakC7Zd2rA/HCnDcbr1DiNZ+/tRshnhzxy/qwmpY8h4qcAYCtQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ku": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "vHmzXcVMe+LNrF9txpdHzpG7XJX65SiN9GQd/Zkt6gsGIIEeECHrkwCN5Jnlkddw2M/b0HS4SNxdR1GrSn7uCA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.lv": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "E1/KUVnYBS1bdOTMNDD7LV/jdoZv/fbWTLPtvwdMtSdqLyRTllv6PGM9xVQoFDYlpvVGtEl/09glCojPHw8ffA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ms-MY": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "vX8oq9HnYmAF7bek4aGgGFJficHDRTLgp/EOiPv9mBZq0i4SA96qVMYSjJ2YTaxs7Eljqit7pfpE2nmBhY5Fnw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.mt": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "pEgTBzUI9hzemF7xrIZigl44LidTUhNu4x/P6M9sAwZjkUF0mMkbpxKkaasOql7lLafKrnszs0xFfaxQyzeuZQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.nb": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "mbs3m6JJq53ssLqVPxNfqSdTxAcZN3njlG8yhJVx83XVedpTe1ECK9aCa8FKVOXv93Gl+yRHF82Hw9T9LWv2hw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.nb-NO": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "AsJxrrVYmIMbKDGe8W6Z6//wKv9dhWH7RsTcEHSr4tQt/80pcNvLi0hgD3fqfTtg0tWKtgch2cLf4prorEV+5A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.nl": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "24b0OUdzJxfoqiHPCtYnR5Y4l/s4Oh7KW7uDp+qX25NMAHLCGog2eRfA7p2kRJp8LvnynwwQxm2p534V9m55wQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.pl": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "17mJNYaBssENVZyQHduiq+bvdXS0nhZJGEXtPKoMhKv3GD//WO0mEfd9wjEBsWCSmWI7bjRqhCidxzN+YtJmsg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.pt": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "8HB8qavcVp2la1GJX6t+G9nDYtylPKzyhxr9LAooIei9MnQvNsjEiIE4QvHoeDZ4weuQ9CsPg1c211XUMVEZ4A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ro": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "psXNOcA6R8fSHoQYhpBTtTTYiOk8OBoN3PKCEDgsJKIyeY5xuK81IBdGi77qGZMu/OwBRQjQCBMtPJb0f4O1+A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.ru": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "zm245xUWrajSN2t9H7BTf84/2APbUkKlUJpcdgsvTdAysr1ag9fi1APu6JEok39RRBXDfNRVZHawQ/U8X0pSvQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.sk": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "Ncw24Vf3ioRnbU4MsMFHafkyYi8JOnTqvK741GftlQvAbULBoTz2+e7JByOaasqeSi0KfTXeegJO+5Wk1c0Mbw==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.sl": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "l8sUy4ciAIbVThWNL0atzTS2HWtv8qJrsGWNlqrEKmPwA4SdKolSqnTes9V89fyZTc2Q43jK8fgzVE2C7t009A==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.sr": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "rnNvhpkOrWEymy7R/MiFv7uef8YO5HuXDyvojZ7JpijHWA5dXuVXooCOiA/3E93fYa3pxDuG2OQe4M/olXbQ7w==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.sr-Latn": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "nuy/ykpk974F8ItoQMS00kJPr2dFNjOSjgzCwfysbu7+gjqHmbLcYs7G4kshLwdA4AsVncxp99LYeJgoh1JF5g==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.sv": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "E53+tpAG0RCp+cSSI7TfBPC+NnsEqUuoSV0sU+rWRXWr9MbRWx1+Zj02XMojqjGzHjjOrBFBBio6m74seFl0AA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.th-TH": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "eSevlJtvs1r4vQarNPfZ2kKDp/xMhuD00tVVzRXkSh1IAZbBJI/x2ydxUOwfK9bEwEp+YjvL1Djx2+kw7ziu7g==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.tr": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "rQ8N+o7yFcFqdbtu1mmbrXFi8TQ+uy+fVH9OPI0CI3Cu1om5hUU/GOMC3hXsTCI6d79y4XX+0HbnD7FT5khegA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.uk": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "2uEfujwXKNm6bdpukaLtEJD+04uUtQD65nSGCetA1fYNizItEaIBUboNfr3GzJxSMQotNwGVM3+nSn8jTd0VSg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.uz-Cyrl-UZ": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "TD3ME2sprAvFqk9tkWrvSKx5XxEMlAn1sjk+cYClSWZlIMhQQ2Bp/w0VjX1Kc5oeKjxRAnR7vFcLUFLiZIDk9Q==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.uz-Latn-UZ": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "/kHAoF4g0GahnugZiEMpaHlxb+W6jCEbWIdsq9/I1k48ULOsl/J0pxZj93lXC3omGzVF1BTVIeAtv5fW06Phsg==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.vi": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "rsQNh9rmHMBtnsUUlJbShMsIMGflZtPmrMM6JNDw20nhsvqfrdcoDD8cMnLAbuSovtc3dP+swRmLQzKmXDTVPA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.zh-CN": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "uH2dWhrgugkCjDmduLdAFO9w1Mo0q07EuvM0QiIZCVm6FMCu/lGv2fpMu4GX+4HLZ6h5T2Pg9FIdDLCPN2a67w==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.zh-Hans": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "WH6IhJ8V1UBG7rZXQk3dZUoP2gsi8a0WkL8xL0sN6WGiv695s8nVcmab9tWz20ySQbuzp0UkSxUQFi5jJHIpOQ==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Humanizer.Core.zh-Hant": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "VIXB7HCUC34OoaGnO3HJVtSv2/wljPhjV7eKH4+TFPgQdJj2lvHNKY41Dtg0Bphu7X5UaXFR4zrYYyo+GNOjbA==", + "dependencies": { + "Humanizer.Core": "[2.14.1]" + } + }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "VrfHrQwvj8p5W3z+PhHCkUMgpPnThJ9ZDeu+AJrFGWFvHSA6sI7iP/k9LjqkdFXsFezlOOZWNAgB6EH9gCCA4g==" + }, + "Microsoft.AspNetCore.Cryptography.KeyDerivation": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "Ct73TxRAPhQ8OqckzgIUPjJ1/WXnQbOnGjqbvTyPmMptdOg9YJ9wXQh9U+da2s6nki54mcZAyonvaKXLsrXhKQ==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "8.0.8" + } + }, + "Microsoft.AspNetCore.Razor.Language": { + "type": "Transitive", + "resolved": "6.0.24", + "contentHash": "kBL6ljTREp/3fk8EKN27mrPy3WTqWUjiqCkKFlCKHUKRO3/9rAasKizX3vPWy4ZTcNsIPmVWUHwjDFmiW4MyNA==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==" + }, + "Microsoft.Build": { + "type": "Transitive", + "resolved": "17.8.3", + "contentHash": "jOxP2DrBZb2zuDO5M8LfI50SCdXlahgUHJ6mH0jz4OBID0F9o+DVggk0CPAONmcbUPo2SsQCFkMaxmHkKLj99Q==", + "dependencies": { + "Microsoft.Build.Framework": "17.8.3", + "Microsoft.NET.StringTools": "17.8.3", + "System.Collections.Immutable": "7.0.0", + "System.Configuration.ConfigurationManager": "7.0.0", + "System.Reflection.Metadata": "7.0.0", + "System.Reflection.MetadataLoadContext": "7.0.0", + "System.Security.Principal.Windows": "5.0.0", + "System.Threading.Tasks.Dataflow": "7.0.0" + } + }, + "Microsoft.Build.Framework": { + "type": "Transitive", + "resolved": "17.8.3", + "contentHash": "NrQZJW8TlKVPx72yltGb8SVz3P5mNRk9fNiD/ao8jRSk48WqIIdCn99q4IjlVmPcruuQ+yLdjNQLL8Rb4c916g==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.4", + "contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==" + }, + "Microsoft.CodeAnalysis.AnalyzerUtilities": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "gyQ70pJ4T7hu/s0+QnEaXtYfeG/JrttGnxHJlrhpxsQjRIUGuRhVwNBtkHHYOrUAZ/l47L98/NiJX6QmTwAyrg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "System.Collections.Immutable": "7.0.0", + "System.Reflection.Metadata": "7.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Features": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "Gpas3l8PE1xz1VDIJNMkYuoFPXtuALxybP04caXh9avC2a0elsoBdukndkJXVZgdKPwraf0a98s7tjqnEk5QIQ==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.8.0]", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "[4.8.0]", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Features": "[4.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "3amm4tq4Lo8/BGvg9p3BJh3S9nKq2wqCXfS7138i69TUpo/bD+XvD0hNurpEBtcNZhi1FyutiomKJqVF39ugYA==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.8.0]", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.Elfie": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "r12elUp4MRjdnRfxEP+xqVSUUfG3yIJTBEJGwbfvF5oU4m0jb9HC0gFG28V/dAkYGMkRmHVi3qvrnBLQSw9X3Q==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.5.0", + "System.Data.DataSetExtensions": "4.5.0" + } + }, + "Microsoft.CodeAnalysis.Features": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "sCVzMtSETGE16KeScwwlVfxaKRbUMSf/cgRPRPMJuou37SLT7XkIBzJu4e7mlFTzpJbfalV5tOcKpUtLO3eJAg==", + "dependencies": { + "Microsoft.CodeAnalysis.AnalyzerUtilities": "3.3.0", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Elfie": "1.0.0", + "Microsoft.CodeAnalysis.Scripting.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]", + "Microsoft.DiaSymReader": "2.0.0", + "System.Text.Json": "7.0.3" + } + }, + "Microsoft.CodeAnalysis.Razor": { + "type": "Transitive", + "resolved": "6.0.24", + "contentHash": "xIAjR6l/1PO2ILT6/lOGYfe8OzMqfqxh1lxFuM4Exluwc2sQhJw0kS7pEyJ0DE/UMYu6Jcdc53DmjOxQUDT2Pg==", + "dependencies": { + "Microsoft.AspNetCore.Razor.Language": "6.0.24", + "Microsoft.CodeAnalysis.CSharp": "4.0.0", + "Microsoft.CodeAnalysis.Common": "4.0.0" + } + }, + "Microsoft.CodeAnalysis.Scripting.Common": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "ysiNNbAASVhV9wEd5oY2x99EwaVYtB13XZRjHsgWT/R1mQkxZF8jWsf7JWaZxD1+jNoz1QCQ6nbe+vr+6QvlFA==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "LXyV+MJKsKRu3FGJA3OmSk40OUIa/dQCFLOnm5X8MNcujx7hzGu8o+zjXlb/cy5xUdZK2UKYb9YaQ2E8m9QehQ==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "System.Composition": "7.0.0", + "System.IO.Pipelines": "7.0.0", + "System.Threading.Channels": "7.0.0" + } + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "5.1.5", + "contentHash": "6kvhQjY5uBCdBccezFD2smfnpQjQ33cZtUZVrNvxlwoBu6uopM5INH6uSgLI7JRLtlQ3bMPwnhMq4kchsXeZ5w==", + "dependencies": { + "Azure.Identity": "1.10.3", + "Microsoft.Data.SqlClient.SNI.runtime": "5.1.1", + "Microsoft.Identity.Client": "4.56.0", + "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "6.0.1", + "System.Diagnostics.DiagnosticSource": "6.0.1", + "System.Runtime.Caching": "6.0.0", + "System.Security.Cryptography.Cng": "5.0.0", + "System.Security.Principal.Windows": "5.0.0", + "System.Text.Encoding.CodePages": "6.0.0", + "System.Text.Encodings.Web": "6.0.0" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "wNGM5ZTQCa2blc9ikXQouybGiyMd6IHPVJvAlBEPtr6JepZEOYeDxGyprYvFVeOxlCXs7avridZQ0nYkHzQWCQ==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "qHInO2EvOcPhjgboP0TGnXM7rASdvWXrw6jAH8Yuz5YP82VTje7d/NKiX1i+dVbE3+G3JuW1kqNVB8yLvsqgYA==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" + }, + "Microsoft.DotNet.Scaffolding.Shared": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "JTPBNbKKjzV4nw2WpS9i5+MCAQqWhe1QA8zsVJTUzR2RIEzqJSM9SyQZckKTfY4tJYGpO44hzAm14uhU0Z2zEA==", + "dependencies": { + "Humanizer": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Features": "4.8.0", + "Microsoft.Extensions.DependencyModel": "8.0.1", + "Mono.TextTemplating": "2.3.1", + "Newtonsoft.Json": "13.0.3", + "NuGet.ProjectModel": "6.11.0" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "iK+jrJzkfbIxutB7or808BPmJtjUEi5O+eSM7cLDwsyde6+3iOujCSfWnrHrLxY3u+EQrJD+aD8DJ6ogPA2Rtw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.8", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.8", + "Microsoft.Extensions.Caching.Memory": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "9mMQkZsfL1c2iifBD8MWRmwy59rvsVtR9NOezJj7+g1j4P7g49MJHd8k8faC/v7d5KuHkQ6KOQiSItvoRt9PXA==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "OlAXMU+VQgLz5y5/SBkLvAa9VeiR3dlJqgIebEEH2M2NGA3evm68/Tv7SLWmSxwnEAtA3nmDEZF2pacK6eXh4Q==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "MmQAMHdjZR8Iyn/FVQrh9weJQTn0HqtKa3vELS9ffQJat/qXgnTam9M9jqvePphjkYp5Scee+Hy+EJR4nmWmOA==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.8", + "Microsoft.Extensions.DependencyModel": "8.0.1", + "Mono.TextTemplating": "2.2.1" + } + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "3WnrwdXxKg4L98cDx0lNEEau8U2lsfuBJCs0Yzht+5XVTmahboM7MukKfQHAzVsHUPszm6ci929S7Qas0WfVHA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "8.0.8", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "w5k/ENj3+BPbmggqh83RRuPhhKcJmW7CmdJuGwdX1eFrmptJwnzKiHfQCPkJAu9df16PSs5YFeWrDgepfqnltA==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "8.0.8", + "Microsoft.EntityFrameworkCore.Relational": "8.0.8", + "Microsoft.Extensions.DependencyModel": "8.0.1" + } + }, + "Microsoft.Extensions.ApiDescription.Server": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "LH4OE/76F6sOCslif7+Xh3fS/wUUrE5ryeXAMcoCnuwOQGT5Smw0p57IgDh/pHgHaGz/e+AmEQb7pRgb++wt0w==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "7pqivmrZDzo1ADPkRwjy+8jtRKWRCPag9qPI+p7sgu7Q4QreWhcvbiWXsbhP+yY8XSiDvZpu2/LWdBv7PnmOpQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "5Ou6varcxLBzQ+Agfm0k0pnH7vrEITYlXMDuE6s7ZHlZHz6/G8XJ3iISZDr5rfwfge6RnXJ1+Wc479mMn52vjA==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.4" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Embedded": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "fefQMlaOIQnc7cn7R/GWCwnA6X0tv5pB9ZKIR6I1JZPmYcoCNY7HtLt0bJPMj+F0StrMkwTk2cPtMGtr9zhkoA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Identity.Core": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "ICzYDXrPFaJWX2c/fp+AvltzVu2LZIe5eK5+JbxndTjGqYEzAAW9jwKSaBq9c3QF0wM9c3SjI80E2Kcn+l9prg==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.KeyDerivation": "8.0.8", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Identity.Stores": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "Lmv3ZQMmanFVacoGChUIP+PhyIsDPFvhbiMX4o/BwJtViZw2ofHXn0CwTlytkBk1zW6S39rGpkfrtAWIUzzLZA==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.Identity.Core": "8.0.8", + "Microsoft.Extensions.Logging": "8.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.56.0", + "contentHash": "rr4zbidvHy9r4NvOAs5hdd964Ao2A0pAeFBJKR95u1CJAVzbd1p6tPTXUZ+5ld0cfThiVSGvz6UHwY6JjraTpA==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.22.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.56.0", + "contentHash": "H12YAzEGK55vZ+QpxUzozhW8ZZtgPDuWvgA0JbdIR9UhMUplj29JhIgE2imuH8W2Nw9D8JKygR1uxRFtpSNcrg==", + "dependencies": { + "Microsoft.Identity.Client": "4.56.0", + "System.IO.FileSystem.AccessControl": "5.0.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.1.2" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.1.2" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.1.2", + "System.IdentityModel.Tokens.Jwt": "7.1.2" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.1.2" + } + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.8.3", + "contentHash": "y6DiuacjlIfXH3XVQG5htf+4oheinZAo7sHbITB3z7yCXQec48f9ZhGSXkr+xn1bfl73Yc3ZQEW2peJ5X68AvQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "1.2.3", + "contentHash": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.VisualStudio.Web.CodeGeneration": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "EffwYVdPlMAoqPCkoezF2nIyBSvgOViz+pK77IBvKWUjPAELxJtMQpYj07soK6igUvCRU1wC91a2YuTNXMqurA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore": "8.0.5" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Core": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "7zvFlCdJESRyv2JKqaygC5eH/5VsLhX792h1dW2NoUt12oQ7RHCmANq7Y0xOUA1hcdxoTySbz4pY6FRCgw1NPw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.VisualStudio.Web.CodeGeneration.Templating": "8.0.5", + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "qd6RAfT95rPUHJ70IswMcPYJYeeey9ZhkRGJ5LV9UE0hxmEo57sBny+X1SFOdtEH8sVQykkMJNtFAl+DPrLb0A==", + "dependencies": { + "Microsoft.DotNet.Scaffolding.Shared": "8.0.5", + "Microsoft.VisualStudio.Web.CodeGeneration.Core": "8.0.5" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Templating": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "PGAjxJA2HOJg2RyJwaK63I4nFEnhrfJ0AG7zhm4fCKa7fYezgV/VmZxv+sTcoYDk3V5etlHYY2OeEoLL6cs1Bg==", + "dependencies": { + "Microsoft.AspNetCore.Razor.Language": "6.0.24", + "Microsoft.CodeAnalysis.CSharp": "4.8.0", + "Microsoft.CodeAnalysis.Razor": "6.0.24", + "Microsoft.VisualStudio.Web.CodeGeneration.Utils": "8.0.5" + } + }, + "Microsoft.VisualStudio.Web.CodeGeneration.Utils": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "V7uAdZ+geRgojDkNzvijiIrJlZQWn5ZYktM1KK/C6gQWyBd7y5TNxINqx+vTZ1I9LDMLd5qkzsOQVI/5PXBjGw==", + "dependencies": { + "Microsoft.Build": "17.8.3", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.8.0", + "Microsoft.DotNet.Scaffolding.Shared": "8.0.5", + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "/5vTiByG62vaoyKh2/+Jho0R45pr8yBQj3kybIsLRRm0ieQp4SDlhKWwIF1C/xSs9MvjNa0cEnoXzNGcGPz0nQ==", + "dependencies": { + "Microsoft.DotNet.Scaffolding.Shared": "8.0.5", + "Microsoft.VisualStudio.Web.CodeGeneration": "8.0.5" + } + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "2.3.1", + "contentHash": "pqYwzNqDL0QK1JFpAjpI/NPqyqLGpHLvVmA5Ec0LaSnbIDtEXxu0td16uunegb7c8xAnlcm4qkbIYUP5FfrFpA==", + "dependencies": { + "System.CodeDom": "5.0.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NuGet.Common": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "T3bCiKUSx8wdYpcqr6Dbx93zAqFp689ee/oa1tH22XI/xl7EUzQ7No/WlE1FUqvEX1+Mqar3wRNAn2O/yxo94g==", + "dependencies": { + "NuGet.Frameworks": "6.11.0" + } + }, + "NuGet.Configuration": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "73QprQqmumFrv3Ooi4YWpRYeBj8jZy9gNdOaOCp4pPInpt41SJJAz/aP4je+StwIJvi5HsgPPecLKekDIQEwKg==", + "dependencies": { + "NuGet.Common": "6.11.0", + "System.Security.Cryptography.ProtectedData": "4.4.0" + } + }, + "NuGet.DependencyResolver.Core": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "SoiPKPooA+IF+iCsX1ykwi3M0e+yBL34QnwIP3ujhQEn1dhlP/N1XsYAnKkJPxV15EZCahuuS4HtnBsZx+CHKA==", + "dependencies": { + "NuGet.Configuration": "6.11.0", + "NuGet.LibraryModel": "6.11.0", + "NuGet.Protocol": "6.11.0" + } + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "Ew/mrfmLF5phsprysHbph2+tdZ10HMHAURavsr/Kx1WhybDG4vmGuoNLbbZMZOqnPRdpyCTc42OKWLoedxpYtA==" + }, + "NuGet.LibraryModel": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "KUV2eeMICMb24OPcICn/wgncNzt6+W+lmFVO5eorTdo1qV4WXxYGyG1NTPiCY+Nrv5H/Ilnv9UaUM2ozqSmnjw==", + "dependencies": { + "NuGet.Common": "6.11.0", + "NuGet.Versioning": "6.11.0" + } + }, + "NuGet.Packaging": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "VmUv2LedVuPY1tfNybORO2I9IuqOzeV7I5JBD+PwNvJq2bAqovi4FCw2cYI0g+kjOJXBN2lAJfrfnqtUOlVJdQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3", + "NuGet.Configuration": "6.11.0", + "NuGet.Versioning": "6.11.0", + "System.Security.Cryptography.Pkcs": "6.0.4" + } + }, + "NuGet.ProjectModel": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "g0KtmDH6fas97WsN73yV2h1F5JT9o6+Y0wlPK+ij9YLKaAXaF6+1HkSaQMMJ+xh9/jCJG9G6nau6InOlb1g48g==", + "dependencies": { + "NuGet.DependencyResolver.Core": "6.11.0" + } + }, + "NuGet.Protocol": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "p5B8oNLLnGhUfMbcS16aRiegj11pD6k+LELyRBqvNFR/pE3yR1XT+g1XS33ME9wvoU+xbCGnl4Grztt1jHPinw==", + "dependencies": { + "NuGet.Packaging": "6.11.0" + } + }, + "NuGet.Versioning": { + "type": "Transitive", + "resolved": "6.11.0", + "contentHash": "v/GGlIj2dd7svplFmASWEueu62veKW0MrMtBaZ7QG8aJTSGv2yE+pgUGhXRcQ4nxNOEq/wLBrz1vkth/1SND7A==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Swashbuckle.AspNetCore": { + "type": "Transitive", + "resolved": "6.2.3", + "contentHash": "cnzQDn0Le+hInsw2SYwlOhOCPXpYi/szcvnyqZJ12v+QyrLBwAmWXBg6RIyHB18s/mLeywC+Rg2O9ndz0IUNYQ==", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "3.0.0", + "Swashbuckle.AspNetCore.Swagger": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerGen": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerUI": "6.2.3" + } + }, + "Swashbuckle.AspNetCore.Swagger": { + "type": "Transitive", + "resolved": "6.2.3", + "contentHash": "qOF7j1sL0bWm8g/qqHVPCvkO3JlVvUIB8WfC98kSh6BT5y5DAnBNctfac7XR5EZf+eD7/WasvANncTqwZYfmWQ==", + "dependencies": { + "Microsoft.OpenApi": "1.2.3" + } + }, + "Swashbuckle.AspNetCore.SwaggerGen": { + "type": "Transitive", + "resolved": "6.2.3", + "contentHash": "+Xq7WdMCCfcXlnbLJVFNgY8ITdP2TRYIlpbt6IKzDw5FwFxdi9lBfNDtcT+/wkKwX70iBBFmXldnnd02/VO72A==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "6.2.3" + } + }, + "Swashbuckle.AspNetCore.SwaggerUI": { + "type": "Transitive", + "resolved": "6.2.3", + "contentHash": "bCRI87uKJVb4G+KURWm8LQrL64St04dEFZcF6gIM67Zc0Sr/N47EO83ybLMYOvfNdO1DCv8xwPcrz9J/VEhQ5g==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + }, + "System.Composition": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "tRwgcAkDd85O8Aq6zHDANzQaq380cek9lbMg5Qma46u5BZXq/G+XvIYmu+UI+BIIZ9zssXLYrkTykEqxxvhcmg==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0", + "System.Composition.Convention": "7.0.0", + "System.Composition.Hosting": "7.0.0", + "System.Composition.Runtime": "7.0.0", + "System.Composition.TypedParts": "7.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2QzClqjElKxgI1jK1Jztnq44/8DmSuTSGGahXqQ4TdEV0h9s2KikQZIgcEqVzR7OuWDFPGLHIprBJGQEPr8fAQ==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IMhTlpCs4HmlD8B+J8/kWfwX7vrBBOs6xyjSTzBlYSs7W4OET4tlkR/Sg9NG8jkdJH9Mymq0qGdYS1VPqRTBnQ==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eB6gwN9S+54jCTBJ5bpwMOVerKeUfGGTYCzz3QgDr1P55Gg/Wb27ShfPIhLMjmZ3MoAKu8uUSv6fcCdYJTN7Bg==", + "dependencies": { + "System.Composition.Runtime": "7.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "aZJ1Zr5Txe925rbo4742XifEyW0MIni1eiUebmcrP3HwLXZ3IbXUj4MFMUH/RmnJOAQiS401leg/2Sz1MkApDw==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "ZK0KNPfbtxVceTwh+oHNGUOYV2WNOHReX2AXipuvkURC7s/jPwoWfsu3SnDBDgofqbiWr96geofdQ2erm/KTHg==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0", + "System.Composition.Hosting": "7.0.0", + "System.Composition.Runtime": "7.0.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "WvRUdlL1lB0dTRZSs5XcQOd5q9MYNk90GkbmRmiCvRHThWiojkpGqWdmEDJdXyHbxG/BhE5hmVbMfRLXW9FJVA==", + "dependencies": { + "System.Diagnostics.EventLog": "7.0.0", + "System.Security.Cryptography.ProtectedData": "7.0.0", + "System.Security.Permissions": "7.0.0" + } + }, + "System.Data.DataSetExtensions": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "221clPs1445HkTBZPL+K9sDBdJRB8UN8rgjO3ztB0CQ26z//fmJXtlsr6whGatscsKGBrhJl5bwJuKSA8mwFOw==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eUDP47obqQm3SFJfP6z+Fx2nJ4KKTQbXB4Q9Uesnzw9SbYdhjyoGXuvDn/gEmFY6N5Z3bFFbpAQGA7m6hrYJCw==" + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==", + "dependencies": { + "Microsoft.Win32.SystemEvents": "7.0.0" + } + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "T6fD00dQ3NTbPDy31m4eQUwKW84s03z0N2C8HpOklyeaDgaJPa/TexP4/SkORMSOwc7WhKifnA6Ya33AkzmafA==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" + } + }, + "System.IO.FileSystem.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", + "dependencies": { + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.6.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "dependencies": { + "System.Collections.Immutable": "7.0.0" + } + }, + "System.Reflection.MetadataLoadContext": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "z9PvtMJra5hK8n+g0wmPtaG7HQRZpTmIPRw5Z0LEemlcdQMHuTD5D7OAY/fZuuz1L9db++QOcDF0gJTLpbMtZQ==", + "dependencies": { + "System.Collections.Immutable": "7.0.0", + "System.Reflection.Metadata": "7.0.0" + } + }, + "System.Runtime.Caching": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==", + "dependencies": { + "System.Configuration.ConfigurationManager": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", + "dependencies": { + "System.Formats.Asn1": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "6.0.4", + "contentHash": "LGbXi1oUJ9QgCNGXRO9ndzBL/GZgANcsURpMhNR8uO+rca47SZmciS3RSQUvlQRwK3QHZSHNOXzoMUASKA+Anw==", + "dependencies": { + "System.Formats.Asn1": "6.0.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "xSPiLNlHT6wAHtugASbKAJwV5GVqQK351crnILAucUioFqqieDN79evO1rku1ckt/GfjIn+b17UaSskoY03JuA==" + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "Vmp0iRmCEno9BWiskOW5pxJ3d9n+jUqKxvX4GhLwFhnQaySZmBN2FuC0N5gjFHgyFMUjC5sfIJ8KZfoJwkcMmA==", + "dependencies": { + "System.Windows.Extensions": "7.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" + }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "BmSJ4b0e2nlplV/RdWVxvH7WECTHACofv06dx/JwOYc0n56eK1jIWdQKNYYsReSO4w8n1QA5stOzSQcfaVBkJg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "bR4qdCmssMMbo9Fatci49An5B1UaVJZHKNq70PRgzoLYIlitb8Tj7ns/Xt5Pz1CkERiTjcVBDU2y1AVrPBYkaw==", + "dependencies": { + "System.Drawing.Common": "7.0.0" + } + }, + "Wangkanai.Hosting": { + "type": "Transitive", + "resolved": "3.2.0", + "contentHash": "beeZI1Qy+VkEiDIQZdfhQiM+npFIlKeDX+a8Fyf09JbTb710o64G31iH432C4xRttZcezbNEf3jkE54X1FXzVg==", + "dependencies": { + "Microsoft.SourceLink.GitHub": "8.0.0", + "Wangkanai.System": "5.0.0" + } + }, + "Wangkanai.System": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "zMzNXq6Ao0vtO4NrzOHqK8OMg0VOL1xTqsCeagyLVdr5k3HNs6ZDsvGBbzNPko7h/T1Q5yIXIP1TcB8xOg/pNA==", + "dependencies": { + "Microsoft.SourceLink.GitHub": "8.0.0" + } + } + } + } +} \ No newline at end of file