From 664d569fd7219f38d9f407d74d8c2350e27d9903 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:52:58 -0500 Subject: [PATCH 1/5] build: add GitHub actions for build, test, code scan, and release (#50) * build: add github actions for build, test, code scan, and release * chore: update github action pinned version * update fortify scan --- .../run_xcodebuild/action.yml | 44 +++++++++ .../run_xcodebuild_test/action.yml | 66 +++++++++++++ .github/workflows/build_liveness.yml | 45 +++++++++ .github/workflows/deploy_liveness.yml | 74 +++++++++++++++ .github/workflows/deploy_release.yml | 17 ++++ .github/workflows/deploy_unstable.yml | 16 ++++ .github/workflows/fortify_scan.yml | 74 +++++++++++++++ .github/workflows/liveness_unit_tests.yml | 14 ++- .github/workflows/release_block_manual_pr.yml | 32 +++++++ .github/workflows/release_kickoff.yml | 21 ++++ CHANGELOG.md | 3 + Gemfile | 8 ++ .../xcshareddata/swiftpm/Package.resolved | 16 ++-- Package.resolved | 4 +- .../FaceLiveness/Utilities/UserAgent.swift | 6 +- fastlane/.gitignore | 1 + fastlane/Fastfile | 95 +++++++++++++++++++ fastlane/Pluginfile | 5 + fastlane/README.md | 40 ++++++++ 19 files changed, 568 insertions(+), 13 deletions(-) create mode 100644 .github/composite_actions/run_xcodebuild/action.yml create mode 100644 .github/composite_actions/run_xcodebuild_test/action.yml create mode 100644 .github/workflows/build_liveness.yml create mode 100644 .github/workflows/deploy_liveness.yml create mode 100644 .github/workflows/deploy_release.yml create mode 100644 .github/workflows/deploy_unstable.yml create mode 100644 .github/workflows/fortify_scan.yml create mode 100644 .github/workflows/release_block_manual_pr.yml create mode 100644 .github/workflows/release_kickoff.yml create mode 100644 CHANGELOG.md create mode 100644 Gemfile create mode 100644 fastlane/.gitignore create mode 100644 fastlane/Fastfile create mode 100644 fastlane/Pluginfile create mode 100644 fastlane/README.md diff --git a/.github/composite_actions/run_xcodebuild/action.yml b/.github/composite_actions/run_xcodebuild/action.yml new file mode 100644 index 00000000..223bdf9e --- /dev/null +++ b/.github/composite_actions/run_xcodebuild/action.yml @@ -0,0 +1,44 @@ +name: 'Run xcodebuild' +description: 'Action runs `xcodebuild build` for the scheme specified' + +inputs: + scheme: + required: true + type: string + project_path: + required: false + type: string + xcode_path: + required: false + type: string + destination: + required: false + type: string + default: 'platform=iOS Simulator,name=iPhone 13,OS=latest' + sdk: + required: false + type: string + default: 'iphonesimulator' + other_flags: + required: false + type: string + default: '' + +runs: + using: "composite" + steps: + - name: Build ${{ inputs.scheme }} + env: + SCHEME: ${{ inputs.scheme }} + PROJECT_PATH: ${{ inputs.project_path }} + XCODE_PATH: ${{ inputs.xcode_path }} + run: | + if [ ! -z "$PROJECT_PATH" ]; then + cd $PROJECT_PATH + fi + if [ ! -z "$XCODE_PATH" ]; then + sudo xcode-select -s $XCODE_PATH + fi + xcodebuild -version + xcodebuild build -scheme $SCHEME -sdk '${{ inputs.sdk }}' -destination '${{ inputs.destination }}' ${{ inputs.other_flags }} | xcpretty --simple --color --report junit && exit ${PIPESTATUS[0]} + shell: bash \ No newline at end of file diff --git a/.github/composite_actions/run_xcodebuild_test/action.yml b/.github/composite_actions/run_xcodebuild_test/action.yml new file mode 100644 index 00000000..00c9e40e --- /dev/null +++ b/.github/composite_actions/run_xcodebuild_test/action.yml @@ -0,0 +1,66 @@ +name: 'Run xcodebuild test' +description: 'Action runs the test for the scheme specified' + +inputs: + scheme: + required: true + type: string + project_path: + required: false + type: string + xcode_path: + required: false + type: string + destination: + required: false + type: string + default: 'platform=iOS Simulator,name=iPhone 13,OS=latest' + sdk: + required: false + type: string + default: 'iphonesimulator' + other_flags: + required: false + type: string + default: '' + generate_coverage: + required: false + type: boolean + default: false + +runs: + using: "composite" + steps: + - name: Test ${{ inputs.scheme }} + env: + SCHEME: ${{ inputs.scheme }} + PROJECT_PATH: ${{ inputs.project_path }} + XCODE_PATH: ${{ inputs.xcode_path }} + run: | + if [ ! -z "$PROJECT_PATH" ]; then + cd $PROJECT_PATH + fi + if [ ! -z "$XCODE_PATH" ]; then + echo "Using Xcode $XCODE_PATH" + sudo xcode-select -s $XCODE_PATH + fi + coverageFlags="" + if [ "${{ inputs.generate_coverage }}" == "true" ]; then + echo "Code Coverage is enabled!" + coverageFlags+="-derivedDataPath Build/ -clonedSourcePackagesDirPath "~/Library/Developer/Xcode/DerivedData/$SCHEME" -enableCodeCoverage YES build test" + fi + xcode-select -p + xcodebuild -version + xcodebuild test -scheme $SCHEME -sdk '${{ inputs.sdk }}' -destination '${{ inputs.destination }}' ${{ inputs.other_flags }} $coverageFlags | xcpretty --simple --color --report junit && exit ${PIPESTATUS[0]} + shell: bash + + - name: Generate Coverage report + if: ${{ inputs.generate_coverage == 'true' }} + run: | + echo "Generating Coverage report..." + cd Build/Build/ProfileData + cd $(ls -d */|head -n 1) + pathCoverage=Build/Build/ProfileData/${PWD##*/}/Coverage.profdata + cd ../../../../ + xcrun llvm-cov export -format="lcov" -instr-profile $pathCoverage Build/Build/Products/Debug-iphonesimulator/$SCHEME.o > Coverage.lcov + shell: bash diff --git a/.github/workflows/build_liveness.yml b/.github/workflows/build_liveness.yml new file mode 100644 index 00000000..923b6846 --- /dev/null +++ b/.github/workflows/build_liveness.yml @@ -0,0 +1,45 @@ +name: Build | Amplify UI Swift Liveness +on: + workflow_call: + inputs: + identifier: + required: true + type: string + workflow_dispatch: + push: + branches-ignore: + - main + - release + +permissions: + contents: read + +concurrency: + group: ${{ inputs.identifier || github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.ref_name != 'main'}} + +jobs: + build-amplify-ui-swift-liveness: + runs-on: macos-13 + timeout-minutes: 20 + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 #v3.5.3 + with: + persist-credentials: false + - name: Build Amplify Swift Liveness UI + uses: ./.github/composite_actions/run_xcodebuild + with: + scheme: AmplifyUILiveness + destination: 'platform=iOS Simulator,name=iPhone 14,OS=16.4' + xcode_path: '/Applications/Xcode_14.3.app' + + confirm-pass: + runs-on: ubuntu-latest + name: Confirm Passing Build Steps + if: ${{ !cancelled() }} + needs: [ build-amplify-ui-swift-liveness ] + env: + EXIT_CODE: ${{ contains(needs.*.result, 'failure') && 1 || 0 }} + steps: + - run: exit $EXIT_CODE + diff --git a/.github/workflows/deploy_liveness.yml b/.github/workflows/deploy_liveness.yml new file mode 100644 index 00000000..0e6e8437 --- /dev/null +++ b/.github/workflows/deploy_liveness.yml @@ -0,0 +1,74 @@ +name: Deploy Liveness +on: + workflow_call: + inputs: + type: + description: 'The type of deployment. Valid values are unstable (default) and release' + default: 'unstable' + required: false + type: string + +permissions: + id-token: write + contents: write + +jobs: + build-amplify-ui-swift-liveness: + name: Build Amplify package + uses: ./.github/workflows/build_liveness.yml + with: + identifier: 'workflow-call-build-liveness' + + unit-tests: + name: Run Unit Tests + uses: ./.github/workflows/liveness_unit_tests.yml + with: + identifier: 'workflow-call-unit-test' + + fortify: + name: Run Fortify Scan + uses: ./.github/workflows/fortify_scan.yml + secrets: inherit + with: + identifier: 'workflow-call-fortify' + + release: + environment: Release + name: Release new ${{ inputs.type }} version + needs: [unit-tests, fortify, build-amplify-ui-swift-liveness] + runs-on: macos-latest + env: + GITHUB_EMAIL: aws-amplify-ops@amazon.com + GITHUB_USER: aws-amplify-ops + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@8c3f20df09ac63af7b3ae3d7c91f105f857d8497 #v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + role-session-name: ${{ format('{0}.release', github.run_id) }} + aws-region: ${{ secrets.AWS_REGION }} + + - id: retrieve-token + name: Retrieve Token + env: + DEPLOY_SECRET_ARN: ${{ secrets.DEPLOY_SECRET_ARN }} + run: | + PAT=$(aws secretsmanager get-secret-value \ + --secret-id "$DEPLOY_SECRET_ARN" \ + | jq -r ".SecretString | fromjson | .Credential") + echo "token=$PAT" >> $GITHUB_OUTPUT + + - name: Checkout repo + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + fetch-depth: 10 + token: ${{steps.retrieve-token.outputs.token}} + + - name: Setup Ruby + uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 + with: + ruby-version: '3.2.1' + bundler-cache: true + + - name: Release Package + run: bundle exec fastlane ${{ inputs.type }} diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml new file mode 100644 index 00000000..68361f09 --- /dev/null +++ b/.github/workflows/deploy_release.yml @@ -0,0 +1,17 @@ +name: Build, Test and Release | Stable version +on: + push: + branches: + release + +permissions: + id-token: write + contents: write + +jobs: + release-stable: + uses: ./.github/workflows/deploy_liveness.yml + with: + type: release + secrets: inherit + diff --git a/.github/workflows/deploy_unstable.yml b/.github/workflows/deploy_unstable.yml new file mode 100644 index 00000000..0b6661a1 --- /dev/null +++ b/.github/workflows/deploy_unstable.yml @@ -0,0 +1,16 @@ +name: Build, Test and Release | Unstable version +on: + push: + branches: + main + +permissions: + id-token: write + contents: write + +jobs: + release-unstable: + uses: ./.github/workflows/deploy_liveness.yml + with: + type: unstable + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/fortify_scan.yml b/.github/workflows/fortify_scan.yml new file mode 100644 index 00000000..bccf9687 --- /dev/null +++ b/.github/workflows/fortify_scan.yml @@ -0,0 +1,74 @@ +name: Fortify Scan +on: + workflow_dispatch: + workflow_call: + inputs: + identifier: + required: true + type: string + push: + branches-ignore: + - main + - release + +permissions: + id-token: write + contents: read + +concurrency: + group: ${{ inputs.identifier || github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.ref_name != 'main'}} + +jobs: + fortify-scan: + runs-on: macos-latest + environment: Fortify + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 #v3.5.3 + with: + persist-credentials: false + + - name: Configure AWS credentials for fetching fortify resources + uses: aws-actions/configure-aws-credentials@8c3f20df09ac63af7b3ae3d7c91f105f857d8497 #v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ secrets.AWS_REGION }} + role-session-name: GHAFortifySession + role-duration-seconds: 900 + mask-aws-account-id: true + + - name: Download License + run: | + aws s3 cp s3://${{ secrets.AWS_S3_FORTIFY_BUCKET }}${{ vars.LICENSE_PATH }} fortify.license + + - name: Download Installer + run: | + aws s3 cp s3://${{ secrets.AWS_S3_FORTIFY_BUCKET }}${{ vars.INSTALLER_PATH }} Fortify_SCA_and_Apps_22.1.1_Mac.tar.gz + tar -xvf Fortify_SCA_and_Apps_22.1.1_Mac.tar.gz + unzip Fortify_SCA_and_Apps_22.1.1_osx_x64.app.zip + + - name: Download Scripts + run: | + aws s3 cp s3://${{ secrets.AWS_S3_FORTIFY_BUCKET }}${{ vars.SCRIPTS_PATH }} liveness_swift_fortify_scan.sh + + - name: Run Installer + run: | + Fortify_SCA_and_Apps_22.1.1_osx_x64.app/Contents/MacOS/installbuilder.sh --mode unattended --installdir ~/amplify-ui-swift-liveness/Fortify --InstallSamples 0 --fortify_license_path fortify.license --MigrateSCA 0 + export PATH=~/amplify-ui-swift-liveness/Fortify/bin:$PATH + fortifyupdate -acceptKey + sourceanalyzer -version + + - name: Run Scan + run: | + export PATH=~/amplify-ui-swift-liveness/Fortify/bin:$PATH + sh ./liveness_swift_fortify_scan.sh Sources + + confirm-pass: + runs-on: ubuntu-latest + name: Confirm Passing Fortify Scan + if: ${{ !cancelled() }} + needs: [ fortify-scan ] + env: + EXIT_CODE: ${{ contains(needs.*.result, 'failure') && 1 || 0 }} + steps: + - run: exit $EXIT_CODE \ No newline at end of file diff --git a/.github/workflows/liveness_unit_tests.yml b/.github/workflows/liveness_unit_tests.yml index b118ea63..a7678b04 100644 --- a/.github/workflows/liveness_unit_tests.yml +++ b/.github/workflows/liveness_unit_tests.yml @@ -1,4 +1,4 @@ -name: Run Unit Tests +name: Run Unit Tests | Amplify UI Swift Liveness on: workflow_dispatch: @@ -12,5 +12,15 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + persist-credentials: false - name: Test FaceLiveness - run: xcodebuild test -scheme AmplifyUILiveness -sdk 'iphonesimulator' -destination 'platform=iOS Simulator,name=iPhone 14,OS=latest' | xcpretty --simple --color --report junit && exit ${PIPESTATUS[0]} + with: + scheme: AmplifyUILiveness + destination: 'platform=iOS Simulator,name=iPhone 14,OS=16.4' + xcode_path: '/Applications/Xcode_14.3.app' + generate_coverage: true + - name: Upload Coverage report to Codecov + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 + with: + flags: 'unittests' \ No newline at end of file diff --git a/.github/workflows/release_block_manual_pr.yml b/.github/workflows/release_block_manual_pr.yml new file mode 100644 index 00000000..b9665b14 --- /dev/null +++ b/.github/workflows/release_block_manual_pr.yml @@ -0,0 +1,32 @@ +# Blocks PRs targeting the release branch that are not created by the release GHA workflow. +# +# This works because workflows cannot trigger other workflows unless they call them directly. +# As a result, this workflow will only run with a PR targeting release is manually created. +# +# https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow + +name: Block Manual PR to Release + +on: + pull_request: + branches: + - release + +permissions: + pull-requests: write + +jobs: + check: + name: Block Manual PR to Release + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + + - name: Close PR + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + gh pr close $PR_NUMBER --comment "Invalid PR! PRs targeting the release branch must be created by the GHA release workflow." diff --git a/.github/workflows/release_kickoff.yml b/.github/workflows/release_kickoff.yml new file mode 100644 index 00000000..f11e765c --- /dev/null +++ b/.github/workflows/release_kickoff.yml @@ -0,0 +1,21 @@ +# Creates a PR to push main to release branch to kick-off the release workflow +name: Release Amplify UI Swift Liveness + +on: + workflow_dispatch: + +permissions: + pull-requests: write + +jobs: + release: + name: Release + runs-on: macos-12 + + steps: + - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + + - name: Create PR to push main to release branch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: "gh pr create --title 'chore: kickoff release' --body 'kickoff release' --head main --base release" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..a7b10c54 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + + diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..00c7ad01 --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +# Gemfile + +source 'https://rubygems.org' + +gem 'xcpretty', '0.3.0' +gem 'fastlane', '2.205.1' +eval_gemfile('fastlane/Pluginfile') + diff --git a/HostApp/HostApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/HostApp/HostApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7f098383..4c89fefe 100644 --- a/HostApp/HostApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/HostApp/HostApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/amplify-swift", "state" : { - "revision" : "5268948fdd0323bb5ef2a487c36d4b64b2a31c83", - "version" : "2.12.0" + "revision" : "3fdd8cbd01b8171dd181a2c74421a95e6643a40c", + "version" : "2.17.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/amplify-ui-swift-liveness", "state" : { - "revision" : "4e0fc1781c8c67d02d23a818b5eea2e81fc3b5fa", - "version" : "1.0.1" + "revision" : "f015e1291a6ba618986777861e9e20ed0ce18967", + "version" : "1.1.1" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git", "state" : { - "revision" : "b036e83716789c13a3480eeb292b70caa54114f2", - "version" : "3.1.0" + "revision" : "c7ec93dcbbcd8abc90c74203937f207a7fcaa611", + "version" : "3.1.1" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", - "version" : "1.5.2" + "revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed", + "version" : "1.5.3" } }, { diff --git a/Package.resolved b/Package.resolved index 595e3b9b..332cdac5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/amplify-swift", "state" : { - "revision" : "bb69fc1febc23dfc539531ce7dd3b51cdf97d813", - "version" : "2.14.1" + "revision" : "76ba8f1ead1cac4d53f313fb8d214c7bc5000551", + "version" : "2.17.1" } }, { diff --git a/Sources/FaceLiveness/Utilities/UserAgent.swift b/Sources/FaceLiveness/Utilities/UserAgent.swift index d0f6b09c..2515b5c3 100644 --- a/Sources/FaceLiveness/Utilities/UserAgent.swift +++ b/Sources/FaceLiveness/Utilities/UserAgent.swift @@ -9,6 +9,10 @@ import UIKit import AWSPluginsCore struct UserAgentValues { + + static let libVersion = "1.1.1" + static let libName = "amplify-ui-swift-face-liveness" + let amplifyVersion: String let os: String let osVersion: String @@ -57,7 +61,7 @@ struct UserAgentValues { swiftVersion: Swift().version(), unameMachine: Device.current.machine.replacingOccurrences(of: ",", with: "_"), locale: Locale.current.identifier, - lib: "lib/amplify-ui-swift-face-liveness/1.1.1", + lib: "lib/\(Self.libName)/\(Self.libVersion)", additionalMetadata: additionalMetadata ) } diff --git a/fastlane/.gitignore b/fastlane/.gitignore new file mode 100644 index 00000000..007b73ee --- /dev/null +++ b/fastlane/.gitignore @@ -0,0 +1 @@ +test_output diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 00000000..6715b218 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,95 @@ +opt_out_usage +default_platform(:ios) + +platform :ios do + before_all do + # Perform a fetch before inferring the next version + # to reduce race conditions with simultaneous pipelines attempting to create the same tag + sh('git', 'fetch', '--tags', '-f') + sh('git', 'fetch') + end + + desc "Create a pre-release version by pushing a tag to GitHub" + lane :unstable do + next_version = calculate_next_canary_version + + UI.message("Releasing unstable version: #{next_version}") + + # Increment all specs and plists + increment_versions(version: next_version) + + # Create tag and push to origin + add_tag(version: next_version) + + end + + desc "Create a release version by building and committing a changelog, pushing a tag to GitHub" + lane :release do + next_version, commits = calculate_next_release_version + + UI.message("Releasing version: #{next_version}") + + # Increment all specs and plists + increment_versions(version: next_version) + + changelog = build_changelog(version: next_version, commits: commits) + + # Commit and push + release_commit(version: next_version) + + # Create tag and push to origin + add_tag(version: next_version) + + post_release(version: next_version, changelog: changelog) + end + + desc "Increment versions" + private_lane :increment_versions do |options| + version = options[:version].to_s + set_key_value(file: "Sources/FaceLiveness/Utilities/UserAgent.swift", key: "libVersion", value: version) + end + + desc "Commit and push" + private_lane :release_commit do |options| + next_version = options[:version] + + sh('git', 'config', '--global', 'user.email', ENV['GITHUB_EMAIL']) + sh('git', 'config', '--global', 'user.name', ENV['GITHUB_USER']) + + commit_message = "chore: release #{next_version} [skip ci]" + sh('git', 'commit', '-am', commit_message) + + # push to origin + sh('git', 'push', 'origin', 'release') + end + + desc "Tag in git and push to GitHub" + private_lane :add_tag do |options| + next_version = options[:version] + next_tag = "#{next_version}" + + add_git_tag(tag: next_tag) + push_git_tags(tag: next_tag) + end + + desc "Post-release" + private_lane :post_release do |options| + version = options[:version].to_s + changelog = options[:changelog] + tag = "#{version}" + + sh('bundle', 'exec', 'swift', 'package', 'update') + + write_changelog(changelog: changelog, path: 'CHANGELOG.md') + + commit_message = "chore: finalize release #{version} [skip ci]" + sh('git', 'commit', '-am', commit_message) + + add_git_tag(tag: tag, force: true) + push_git_tags(tag: tag, force: true) + + # push to origin + sh('git', 'push', 'origin', 'release') + sh('git', 'push', 'origin', 'release:main') + end +end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 00000000..9750a5bd --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-release_actions', git: 'https://github.com/aws-amplify/amplify-ci-support', branch: 'main', glob: 'src/fastlane/release_actions/*.gemspec' \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 00000000..1571e7ed --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,40 @@ +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 + +## iOS + +### ios unstable + +```sh +[bundle exec] fastlane ios unstable +``` + +Create a pre-release version by pushing a tag to GitHub + +### ios release + +```sh +[bundle exec] fastlane ios release +``` + +Create a release version by building and committing a changelog, pushing a tag to GitHub + +---- + +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). From f3bef7c4b91991d751d35c710a68983bd7604e98 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Thu, 14 Sep 2023 14:23:49 -0700 Subject: [PATCH 2/5] ci: add dependency review workflow (#45) * ci: add dependency review workflow * Update dependency-review.yml --- .github/workflows/dependency-review.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/dependency-review.yml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..27ebe3b7 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,24 @@ +name: Dependency Review + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + with: + persist-credentials: false + + - name: Dependency Review + uses: actions/dependency-review-action@7d90b4f05fea31dde1c4a1fb3fa787e197ea93ab # v3.0.7 + with: + config-file: aws-amplify/amplify-ci-support/.github/dependency-review-config.yml@main From 4ff64dad93790e04e525343a3fcebd4bf1fba8f1 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Wed, 20 Sep 2023 15:15:33 -0500 Subject: [PATCH 3/5] chore: clean up github actions (#51) --- .github/workflows/deploy_liveness.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy_liveness.yml b/.github/workflows/deploy_liveness.yml index 0e6e8437..37fa3028 100644 --- a/.github/workflows/deploy_liveness.yml +++ b/.github/workflows/deploy_liveness.yml @@ -47,6 +47,7 @@ jobs: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} role-session-name: ${{ format('{0}.release', github.run_id) }} aws-region: ${{ secrets.AWS_REGION }} + mask-aws-account-id: true - id: retrieve-token name: Retrieve Token From 20a8ad8ded5fad569b7340dbab32de8f2b00ce29 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:37:12 -0500 Subject: [PATCH 4/5] chore: improve localization support (#54) * chore: improve localization support * chore: revert integration test change * chore: fix code comment typo * revert project file --- .../Utilities/LivenessLocalizedStrings.swift | 104 +++++++++++ .../LocalizedStringKey+Liveness.swift | 166 ------------------ .../Utilities/String+Localizable.swift | 27 +++ Sources/FaceLiveness/Views/CloseButton.swift | 2 +- .../Views/GetReadyPage/GetReadyPageView.swift | 34 ++-- .../OvalIllustrationExamples.swift | 8 +- .../InstructionContainerView.swift | 32 +--- .../Views/Instruction/InstructionView.swift | 4 +- .../FaceLiveness/Views/RecordingButton.swift | 2 +- Sources/FaceLiveness/Views/WarningBox.swift | 12 +- .../IntegrationTestApp/Localizable.xcstrings | 5 + 11 files changed, 175 insertions(+), 221 deletions(-) create mode 100644 Sources/FaceLiveness/Utilities/LivenessLocalizedStrings.swift delete mode 100644 Sources/FaceLiveness/Utilities/LocalizedStringKey+Liveness.swift create mode 100644 Sources/FaceLiveness/Utilities/String+Localizable.swift create mode 100644 Tests/IntegrationTestApp/IntegrationTestApp/Localizable.xcstrings diff --git a/Sources/FaceLiveness/Utilities/LivenessLocalizedStrings.swift b/Sources/FaceLiveness/Utilities/LivenessLocalizedStrings.swift new file mode 100644 index 00000000..ba45e269 --- /dev/null +++ b/Sources/FaceLiveness/Utilities/LivenessLocalizedStrings.swift @@ -0,0 +1,104 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import SwiftUI + +enum LocalizedStrings { + /// en = "Liveness Check" + static let get_ready_page_title = "amplify_ui_liveness_get_ready_page_title".localized() + + /// en = "You will go through a face verification process to prove that you are a real person. Your screen's brightness will temporarily be set to 100% for highest accuracy." + static let get_ready_page_description = "amplify_ui_liveness_get_ready_page_description".localized() + + /// en = "Photosensitivity Warning" + static let get_ready_photosensitivity_title = "amplify_ui_liveness_get_ready_photosensitivity_title".localized() + + /// en = "This check displays colored lights. Use caution if you are photosensitive." + static let get_ready_photosensitivity_description = "amplify_ui_liveness_get_ready_photosensitivity_description".localized() + + /// en = "Photosensitivity Information" + static let get_ready_photosensitivity_icon_a11y = "amplify_ui_liveness_get_ready_photosensitivity_icon_a11y".localized() + + /// en = "Photosensitivity warning" + static let get_ready_photosensitivity_dialog_title = "amplify_ui_liveness_get_ready_photosensitivity_dialog_title".localized() + + /// en = "A small percentage of individuals may experience epileptic seizures when exposed to colored lights. Use caution if you, or anyone in your family, have an epileptic condition." + static let get_ready_photosensitivity_dialog_description = "amplify_ui_liveness_get_ready_photosensitivity_dialog_description".localized() + + /// en = "Follow the instructions to complete the check:" + static let get_ready_steps_title = "amplify_ui_liveness_get_ready_steps_title".localized() + + /// en = "Make sure your face is not covered with sunglasses or a mask." + static let get_ready_face_not_covered = "amplify_ui_liveness_get_ready_face_not_covered".localized() + + /// en = "Move to a well-lit place that is not in direct sunlight." + static let get_ready_lighting = "amplify_ui_liveness_get_ready_lighting".localized() + + /// en = "When an oval appears, fill the oval with your face in it." + static let get_ready_fit_face = "amplify_ui_liveness_get_ready_fit_face".localized() + + /// en = "Begin Check" + static let get_ready_begin_check = "amplify_ui_liveness_get_ready_begin_check".localized() + + /// en = "Illustration demonstrating good fit of face in oval." + static let get_ready_illustration_good_fit_a11y = "amplify_ui_liveness_get_ready_illustration_good_fit_a11y".localized() + + /// en = "Illustration demonstrating face too far from screen." + static let get_ready_illustration_too_far_a11y = "amplify_ui_liveness_get_ready_illustration_too_far_a11y".localized() + + /// en = "REC" + static let challenge_recording_indicator_label = "amplify_ui_liveness_challenge_recording_indicator_label".localized() + + /// en = "Hold face in oval for colored lights." + static let challenge_instruction_hold_face_during_freshness = "amplify_ui_liveness_challenge_instruction_hold_face_during_freshness".localized() + + /// en = "Move back" + static let challenge_instruction_move_face_back = "amplify_ui_liveness_challenge_instruction_move_face_back".localized() + + /// en = "Move closer" + static let challenge_instruction_move_face_closer = "amplify_ui_liveness_challenge_instruction_move_face_closer".localized() + + /// en = "Move closer" + static let challenge_instruction_move_face = "amplify_ui_liveness_challenge_instruction_move_face".localized() + + /// en = "Hold still" + static let challenge_instruction_hold_still = "amplify_ui_liveness_challenge_instruction_hold_still".localized() + + /// en = "Ensure only one face is in front of camera" + static let challenge_instruction_multiple_faces_detected = "amplify_ui_liveness_challenge_instruction_multiple_faces_detected".localized() + + /// en = "Connecting..." + static let challenge_connecting = "amplify_ui_liveness_challenge_connecting".localized() + + /// en = "Verifying" + static let challenge_verifying = "amplify_ui_liveness_challenge_verifying".localized() + + /// en = "Cancel Challenge" + static let challenge_cancel_a11y = "amplify_ui_liveness_challenge_cancel_a11y".localized() + + /// en = "Change Your Camera Settings" + static let camera_setting_alert_title = "amplify_ui_liveness_camera_setting_alert_title".localized() + + /// en = "Allow camera permission in settings." + static let camera_setting_alert_message = "amplify_ui_liveness_camera_setting_alert_message".localized() + + /// en = "Update Setting" + static let camera_setting_alert_update_setting_button_text = "amplify_ui_liveness_camera_setting_alert_update_setting_button_text".localized() + + /// en = "Not Now" + static let camera_setting_alert_not_now_button_text = "amplify_ui_liveness_camera_setting_alert_not_now_button_text".localized() + + /// en = "Close" + static let close_button_a11y = "amplify_ui_liveness_close_button_a11y".localized() + + /// en = "Good fit" + static let get_ready_good_fit_example = "amplify_ui_liveness_get_ready_good_fit_example".localized() + + /// en = "Too far" + static let get_ready_too_far_example = "amplify_ui_liveness_get_ready_too_far_example".localized() + +} diff --git a/Sources/FaceLiveness/Utilities/LocalizedStringKey+Liveness.swift b/Sources/FaceLiveness/Utilities/LocalizedStringKey+Liveness.swift deleted file mode 100644 index 77f7fcff..00000000 --- a/Sources/FaceLiveness/Utilities/LocalizedStringKey+Liveness.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import SwiftUI - -extension LocalizedStringKey { - /// en = "Liveness Check" - static let get_ready_page_title = LocalizedStringKey( - "amplify_ui_liveness_get_ready_page_title" - ) - - /// en = "You will go through a face verification process to prove that you are a real person. Your screen's brightness will temporarily be set to 100% for highest accuracy." - static let get_ready_page_description = LocalizedStringKey( - "amplify_ui_liveness_get_ready_page_description" - ) - - /// en = "Photosensitivity Warning" - static let get_ready_photosensitivity_title = LocalizedStringKey( - "amplify_ui_liveness_get_ready_photosensitivity_title" - ) - - /// en = "This check displays colored lights. Use caution if you are photosensitive." - static let get_ready_photosensitivity_description = LocalizedStringKey( - "amplify_ui_liveness_get_ready_photosensitivity_description" - ) - - /// en = "Photosensitivity Information" - static let get_ready_photosensitivity_icon_a11y = LocalizedStringKey( - "amplify_ui_liveness_get_ready_photosensitivity_icon_a11y" - ) - - /// en = "Photosensitivity warning" - static let get_ready_photosensitivity_dialog_title = LocalizedStringKey( - "amplify_ui_liveness_get_ready_photosensitivity_dialog_title" - ) - - /// en = "A small percentage of individuals may experience epileptic seizures when exposed to colored lights. Use caution if you, or anyone in your family, have an epileptic condition." - static let get_ready_photosensitivity_dialog_description = LocalizedStringKey( - "amplify_ui_liveness_get_ready_photosensitivity_dialog_description" - ) - - /// en = "Follow the instructions to complete the check:" - static let get_ready_steps_title = LocalizedStringKey( - "amplify_ui_liveness_get_ready_steps_title" - ) - - /// en = "Make sure your face is not covered with sunglasses or a mask." - static let get_ready_face_not_covered = LocalizedStringKey( - "amplify_ui_liveness_get_ready_face_not_covered" - ) - - /// en = "Move to a well-lit place that is not in direct sunlight." - static let get_ready_lighting = LocalizedStringKey( - "amplify_ui_liveness_get_ready_lighting" - ) - - /// en = "When an oval appears, fill the oval with your face in it." - static let get_ready_fit_face = LocalizedStringKey( - "amplify_ui_liveness_get_ready_fit_face" - ) - - /// en = "Begin Check" - static let get_ready_begin_check = LocalizedStringKey( - "amplify_ui_liveness_get_ready_begin_check" - ) - - /// en = "Illustration demonstrating good fit of face in oval." - static let get_ready_illustration_good_fit_a11y = LocalizedStringKey( - "amplify_ui_liveness_get_ready_illustration_good_fit_a11y" - ) - - /// en = "Illustration demonstrating face too far from screen." - static let get_ready_illustration_too_far_a11y = LocalizedStringKey( - "amplify_ui_liveness_get_ready_illustration_too_far_a11y" - ) - - /// en = "REC" - static let challenge_recording_indicator_label = LocalizedStringKey( - "amplify_ui_liveness_challenge_recording_indicator_label" - ) - - /// en = "Hold face in oval for colored lights." - static let challenge_instruction_hold_face_during_freshness = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_hold_face_during_freshness" - ) - - /// en = "Move back" - static let challenge_instruction_move_face_back = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_move_face_back" - ) - - /// en = "Move closer" - static let challenge_instruction_move_face_closer = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_move_face_closer" - ) - - /// en = "Move closer" - static let challenge_instruction_move_face = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_move_face" - ) - - /// en = "Hold still" - static let challenge_instruction_hold_still = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_hold_still" - ) - - /// en = "Ensure only one face is in front of camera" - static let challenge_instruction_multiple_faces_detected = LocalizedStringKey( - "amplify_ui_liveness_challenge_instruction_multiple_faces_detected" - ) - - /// en = "Connecting..." - static let challenge_connecting = LocalizedStringKey( - "amplify_ui_liveness_challenge_connecting" - ) - - /// en = "Verifying" - static let challenge_verifying = LocalizedStringKey( - "amplify_ui_liveness_challenge_verifying" - ) - - /// en = "Cancel Challenge" - static let challenge_cancel_a11y = LocalizedStringKey( - "amplify_ui_liveness_challenge_cancel_a11y" - ) - - /// en = "Change Your Camera Settings" - static let camera_setting_alert_title = LocalizedStringKey( - "amplify_ui_liveness_camera_setting_alert_title" - ) - - /// en = "Allow camera permission in settings." - static let camera_setting_alert_message = LocalizedStringKey( - "amplify_ui_liveness_camera_setting_alert_message" - ) - - /// en = "Update Setting" - static let camera_setting_alert_update_setting_button_text = LocalizedStringKey( - "amplify_ui_liveness_camera_setting_alert_update_setting_button_text" - ) - - /// en = "Not Now" - static let camera_setting_alert_not_now_button_text = LocalizedStringKey( - "amplify_ui_liveness_camera_setting_alert_not_now_button_text" - ) - - /// en = "Close" - static let close_button_a11y = LocalizedStringKey( - "amplify_ui_liveness_close_button_a11y" - ) - - /// en = "Good fit" - static let get_ready_good_fit_example = LocalizedStringKey( - "amplify_ui_liveness_get_ready_good_fit_example" - ) - - /// en = "Too far" - static let get_ready_too_far_example = LocalizedStringKey( - "amplify_ui_liveness_get_ready_too_far_example" - ) - -} diff --git a/Sources/FaceLiveness/Utilities/String+Localizable.swift b/Sources/FaceLiveness/Utilities/String+Localizable.swift new file mode 100644 index 00000000..b81091dc --- /dev/null +++ b/Sources/FaceLiveness/Utilities/String+Localizable.swift @@ -0,0 +1,27 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +extension String { + /// Looks for a localized value using this value as the key. + /// If no localization is found in the current app's bundle, + /// it defaults to the one provided by Liveness + func localized(comment: String = "") -> String { + let defaultValue = NSLocalizedString(self, bundle: .module, comment: "") + return NSLocalizedString( + self, + bundle: .main, + value: defaultValue, + comment: "" + ) + } + + func localized(using arguments: CVarArg...) -> String { + return String(format: localized(), arguments) + } +} diff --git a/Sources/FaceLiveness/Views/CloseButton.swift b/Sources/FaceLiveness/Views/CloseButton.swift index 19dddc7c..dee45c40 100644 --- a/Sources/FaceLiveness/Views/CloseButton.swift +++ b/Sources/FaceLiveness/Views/CloseButton.swift @@ -20,7 +20,7 @@ struct CloseButton: View { .frame(width: 44, height: 44) .background(Color.livenessBackground) .clipShape(Circle()) - .accessibilityLabel(Text(.close_button_a11y, bundle: .module)) + .accessibilityLabel(Text(LocalizedStrings.close_button_a11y)) } ) } diff --git a/Sources/FaceLiveness/Views/GetReadyPage/GetReadyPageView.swift b/Sources/FaceLiveness/Views/GetReadyPage/GetReadyPageView.swift index bf8b31e1..404d261e 100644 --- a/Sources/FaceLiveness/Views/GetReadyPage/GetReadyPageView.swift +++ b/Sources/FaceLiveness/Views/GetReadyPage/GetReadyPageView.swift @@ -26,23 +26,23 @@ struct GetReadyPageView: View { VStack { ScrollView { VStack(alignment: .leading) { - Text(.get_ready_page_title, bundle: .module) + Text(LocalizedStrings.get_ready_page_title) .font(.system(size: 34, weight: .semibold)) .accessibilityAddTraits(.isHeader) .padding(.bottom, 8) - Text(.get_ready_page_description, bundle: .module) + Text(LocalizedStrings.get_ready_page_description) .padding(.bottom, 8) WarningBox( - titleText: .get_ready_photosensitivity_title, - bodyText: .get_ready_photosensitivity_description, + titleText: LocalizedStrings.get_ready_photosensitivity_title, + bodyText: LocalizedStrings.get_ready_photosensitivity_description, popoverContent: { photosensitivityWarningPopoverContent } ) .accessibilityElement(children: .combine) .padding(.bottom, 8) - Text(.get_ready_steps_title, bundle: .module) + Text(LocalizedStrings.get_ready_steps_title) .fontWeight(.semibold) .padding(.bottom, 16) @@ -59,15 +59,15 @@ struct GetReadyPageView: View { } .alert(isPresented: $displayingCameraPermissionsNeededAlert) { Alert( - title: Text(.camera_setting_alert_title, bundle: .module), - message: Text(.camera_setting_alert_message, bundle: .module), + title: Text(LocalizedStrings.camera_setting_alert_title), + message: Text(LocalizedStrings.camera_setting_alert_message), primaryButton: .default( - Text(.camera_setting_alert_update_setting_button_text, bundle: .module).bold(), + Text(LocalizedStrings.camera_setting_alert_update_setting_button_text).bold(), action: { goToSettingsAppPage() }), secondaryButton: .default( - Text(.camera_setting_alert_not_now_button_text, bundle: .module) + Text(LocalizedStrings.camera_setting_alert_not_now_button_text) ) ) } @@ -83,7 +83,7 @@ struct GetReadyPageView: View { Button( action: onBegin, label: { - Text(.get_ready_begin_check, bundle: .module) + Text(LocalizedStrings.get_ready_begin_check) .foregroundColor(.livenessPrimaryLabel) .frame(maxWidth: .infinity) } @@ -98,21 +98,21 @@ struct GetReadyPageView: View { private var photosensitivityWarningPopoverContent: some View { VStack { - Text(.get_ready_photosensitivity_dialog_title, bundle: .module) + Text(LocalizedStrings.get_ready_photosensitivity_dialog_title) .font(.system(size: 20, weight: .medium)) .frame(alignment: .center) .padding() - Text(.get_ready_photosensitivity_dialog_description, bundle: .module) + Text(LocalizedStrings.get_ready_photosensitivity_dialog_description) .padding() Spacer() } } private func steps() -> some View { - func step(number: Int, text: LocalizedStringKey) -> some View { + func step(number: Int, text: String) -> some View { HStack(alignment: .top) { Text("\(number).") - Text(text, bundle: .module) + Text(text) } } @@ -120,13 +120,13 @@ struct GetReadyPageView: View { alignment: .leading, spacing: 16 ) { - step(number: 1, text: .get_ready_fit_face) + step(number: 1, text: LocalizedStrings.get_ready_fit_face) .accessibilityElement(children: .combine) - step(number: 2, text: .get_ready_face_not_covered) + step(number: 2, text: LocalizedStrings.get_ready_face_not_covered) .accessibilityElement(children: .combine) - step(number: 3, text: .get_ready_lighting) + step(number: 3, text: LocalizedStrings.get_ready_lighting) .accessibilityElement(children: .combine) } } diff --git a/Sources/FaceLiveness/Views/GetReadyPage/OvalIllustrationExamples.swift b/Sources/FaceLiveness/Views/GetReadyPage/OvalIllustrationExamples.swift index 763ff611..b188cff8 100644 --- a/Sources/FaceLiveness/Views/GetReadyPage/OvalIllustrationExamples.swift +++ b/Sources/FaceLiveness/Views/GetReadyPage/OvalIllustrationExamples.swift @@ -12,23 +12,23 @@ struct OvalIllustrationExamples: View { HStack(spacing: 16) { OvalIllustrationView( icon: .checkmark(backgroundColor: .hex("#365E3D")), - text: { Text(.get_ready_good_fit_example, bundle: .module) }, + text: { Text(LocalizedStrings.get_ready_good_fit_example) }, primaryColor: .hex("#365E3D"), secondaryColor: .hex("#D6F5DB"), illustration: { Image("illustration_face_good_fit", bundle: .module) } ) .accessibilityElement(children: .ignore) - .accessibilityLabel(Text(.get_ready_illustration_good_fit_a11y, bundle: .module)) + .accessibilityLabel(Text(LocalizedStrings.get_ready_illustration_good_fit_a11y)) OvalIllustrationView( icon: .xmark(backgroundColor: .hex("#660000")), - text: { Text(.get_ready_too_far_example, bundle: .module) }, + text: { Text(LocalizedStrings.get_ready_too_far_example) }, primaryColor: .hex("#660000"), secondaryColor: .hex("#F5BCBC"), illustration: { Image("illustration_face_too_far", bundle: .module) } ) .accessibilityElement(children: .ignore) - .accessibilityLabel(Text(.get_ready_illustration_too_far_a11y, bundle: .module)) + .accessibilityLabel(Text(LocalizedStrings.get_ready_illustration_too_far_a11y)) Spacer() } diff --git a/Sources/FaceLiveness/Views/Instruction/InstructionContainerView.swift b/Sources/FaceLiveness/Views/Instruction/InstructionContainerView.swift index 028eb386..a1ae560d 100644 --- a/Sources/FaceLiveness/Views/Instruction/InstructionContainerView.swift +++ b/Sources/FaceLiveness/Views/Instruction/InstructionContainerView.swift @@ -15,7 +15,7 @@ struct InstructionContainerView: View { switch viewModel.livenessState.state { case .displayingFreshness: InstructionView( - text: .challenge_instruction_hold_still, + text: LocalizedStrings.challenge_instruction_hold_still, backgroundColor: .livenessPrimaryBackground, textColor: .livenessPrimaryLabel, font: .title @@ -23,17 +23,13 @@ struct InstructionContainerView: View { .onAppear { UIAccessibility.post( notification: .announcement, - argument: NSLocalizedString( - "amplify_ui_liveness_challenge_instruction_hold_still", - bundle: .module, - comment: "" - ) + argument: LocalizedStrings.challenge_instruction_hold_still ) } case .awaitingFaceInOvalMatch(.faceTooClose, _): InstructionView( - text: .challenge_instruction_move_face_back, + text: LocalizedStrings.challenge_instruction_move_face_back, backgroundColor: .livenessErrorBackground, textColor: .livenessErrorLabel, font: .title @@ -41,11 +37,7 @@ struct InstructionContainerView: View { .onAppear { UIAccessibility.post( notification: .announcement, - argument: NSLocalizedString( - "amplify_ui_liveness_challenge_instruction_move_face_back", - bundle: .module, - comment: "" - ) + argument: LocalizedStrings.challenge_instruction_move_face_back ) } @@ -67,7 +59,7 @@ struct InstructionContainerView: View { .frame(width: 200, height: 30) case .recording(ovalDisplayed: true): InstructionView( - text: .challenge_instruction_move_face_closer, + text: LocalizedStrings.challenge_instruction_move_face_closer, backgroundColor: .livenessPrimaryBackground, textColor: .livenessPrimaryLabel, font: .title @@ -75,11 +67,7 @@ struct InstructionContainerView: View { .onAppear { UIAccessibility.post( notification: .announcement, - argument: NSLocalizedString( - "amplify_ui_liveness_challenge_instruction_move_face_closer", - bundle: .module, - comment: "" - ) + argument: LocalizedStrings.challenge_instruction_move_face_closer ) } @@ -98,17 +86,13 @@ struct InstructionContainerView: View { ) case .completedDisplayingFreshness: InstructionView( - text: .challenge_verifying, + text: LocalizedStrings.challenge_verifying, backgroundColor: .livenessBackground ) .onAppear { UIAccessibility.post( notification: .announcement, - argument: NSLocalizedString( - "amplify_ui_liveness_challenge_verifying", - bundle: .module, - comment: "" - ) + argument: LocalizedStrings.challenge_verifying ) } default: diff --git a/Sources/FaceLiveness/Views/Instruction/InstructionView.swift b/Sources/FaceLiveness/Views/Instruction/InstructionView.swift index 4fe7fb5e..5311387b 100644 --- a/Sources/FaceLiveness/Views/Instruction/InstructionView.swift +++ b/Sources/FaceLiveness/Views/Instruction/InstructionView.swift @@ -8,13 +8,13 @@ import SwiftUI struct InstructionView: View { - let text: LocalizedStringKey + let text: String let backgroundColor: Color var textColor: Color = .livenessLabel var font: Font = .body var body: some View { - Text(text, bundle: .module) + Text(text) .foregroundColor(textColor) .font(font) .padding(12) diff --git a/Sources/FaceLiveness/Views/RecordingButton.swift b/Sources/FaceLiveness/Views/RecordingButton.swift index 0b55ee96..0a157278 100644 --- a/Sources/FaceLiveness/Views/RecordingButton.swift +++ b/Sources/FaceLiveness/Views/RecordingButton.swift @@ -13,7 +13,7 @@ struct RecordingButton: View { Circle() .foregroundColor(.hex("#F92626")) .frame(width: 17, height: 17) - Text(.challenge_recording_indicator_label, bundle: .module) + Text(LocalizedStrings.challenge_recording_indicator_label) .font(.system(size: 12)) .fontWeight(.bold) } diff --git a/Sources/FaceLiveness/Views/WarningBox.swift b/Sources/FaceLiveness/Views/WarningBox.swift index fe46b9cd..050c2aa4 100644 --- a/Sources/FaceLiveness/Views/WarningBox.swift +++ b/Sources/FaceLiveness/Views/WarningBox.swift @@ -9,13 +9,13 @@ import SwiftUI struct WarningBox: View { @State var isPresentingPopover = false - let titleText: LocalizedStringKey - let bodyText: LocalizedStringKey + let titleText: String + let bodyText: String let popoverContent: PopoverView init( - titleText: LocalizedStringKey, - bodyText: LocalizedStringKey, + titleText: String, + bodyText: String, @ViewBuilder popoverContent: () -> PopoverView ) { self.titleText = titleText @@ -26,11 +26,11 @@ struct WarningBox: View { var body: some View { HStack { VStack(alignment: .leading) { - Text(titleText, bundle: .module) + Text(titleText) .fontWeight(.semibold) .foregroundColor(.livenessWarningLabel) - Text(bodyText, bundle: .module) + Text(bodyText) .foregroundColor(.livenessWarningLabel) } Spacer() diff --git a/Tests/IntegrationTestApp/IntegrationTestApp/Localizable.xcstrings b/Tests/IntegrationTestApp/IntegrationTestApp/Localizable.xcstrings new file mode 100644 index 00000000..8a470103 --- /dev/null +++ b/Tests/IntegrationTestApp/IntegrationTestApp/Localizable.xcstrings @@ -0,0 +1,5 @@ +{ + "sourceLanguage" : "en", + "strings" : {}, + "version" : "1.0" +} From 21f31d0cc4f0e384bf1356015c503e8333254f79 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:20:42 -0500 Subject: [PATCH 5/5] test: fix unit test GitHub action workflow (#55) * chore: fix unit test GH action * chore: fix unit test GH action * chore: fix unit test GH action * revert test branch --- .github/workflows/liveness_unit_tests.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/liveness_unit_tests.yml b/.github/workflows/liveness_unit_tests.yml index a7678b04..df179fec 100644 --- a/.github/workflows/liveness_unit_tests.yml +++ b/.github/workflows/liveness_unit_tests.yml @@ -8,19 +8,18 @@ on: branches: [ main ] jobs: - build: - runs-on: macos-latest + test-iOS: + name: Liveness iOS Unit Tests + runs-on: macos-13 + timeout-minutes: 30 steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: persist-credentials: false - name: Test FaceLiveness + continue-on-error: false + uses: ./.github/composite_actions/run_xcodebuild_test with: scheme: AmplifyUILiveness destination: 'platform=iOS Simulator,name=iPhone 14,OS=16.4' - xcode_path: '/Applications/Xcode_14.3.app' - generate_coverage: true - - name: Upload Coverage report to Codecov - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 - with: - flags: 'unittests' \ No newline at end of file + xcode_path: '/Applications/Xcode_14.3.app' \ No newline at end of file