diff --git a/.circleci/fastlane/Fastfile b/.circleci/fastlane/Fastfile index e5bcae5e16..4e5a354947 100644 --- a/.circleci/fastlane/Fastfile +++ b/.circleci/fastlane/Fastfile @@ -124,4 +124,4 @@ def analyze_scheme(scheme) system('mv ../libs/SmartStore/clangReport/StaticAnalyzer/SmartStore ../clangReport/StaticAnalyzer/') system('mv ../libs/MobileSync/clangReport/StaticAnalyzer/MobileSync ../clangReport/StaticAnalyzer/') end -end +end \ No newline at end of file diff --git a/.github/DangerFiles/Gemfile b/.github/DangerFiles/Gemfile new file mode 100644 index 0000000000..5ea887c6ef --- /dev/null +++ b/.github/DangerFiles/Gemfile @@ -0,0 +1,5 @@ +source("https://rubygems.org") + +gem 'danger' +gem 'plist' +gem 'danger-xcode_summary' \ No newline at end of file diff --git a/.github/DangerFiles/Gemfile.lock b/.github/DangerFiles/Gemfile.lock new file mode 100644 index 0000000000..297bbfdbbb --- /dev/null +++ b/.github/DangerFiles/Gemfile.lock @@ -0,0 +1,82 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.2.0) + claide (1.1.0) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + colored2 (3.1.2) + cork (0.3.0) + colored2 (~> 3.1) + danger (9.5.1) + base64 (~> 0.2) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (>= 0.9.0, < 3.0) + faraday-http-cache (~> 2.0) + git (~> 1.13) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + octokit (>= 4.0) + pstore (~> 0.1) + terminal-table (>= 1, < 4) + danger-plugin-api (1.0.0) + danger (> 2.0) + danger-xcode_summary (1.3.1) + danger-plugin-api (~> 1.0) + xcresult (~> 0.2.2) + faraday (2.12.0) + faraday-net_http (>= 2.0, < 3.4) + json + logger + faraday-http-cache (2.5.1) + faraday (>= 0.8) + faraday-net_http (3.3.0) + net-http + git (1.19.1) + addressable (~> 2.8) + rchardet (~> 1.8) + json (2.7.5) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + logger (1.6.1) + nap (1.1.0) + net-http (0.4.1) + uri + octokit (9.2.0) + faraday (>= 1, < 3) + sawyer (~> 0.9) + open4 (1.3.4) + plist (3.7.1) + pstore (0.1.3) + public_suffix (6.0.1) + rchardet (1.8.0) + rexml (3.3.9) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.6.0) + uri (0.13.1) + xcresult (0.2.2) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + danger + danger-xcode_summary + plist + +BUNDLED WITH + 2.5.22 \ No newline at end of file diff --git a/.github/DangerFiles/StaticAnalysis.rb b/.github/DangerFiles/StaticAnalysis.rb new file mode 100644 index 0000000000..522759b543 --- /dev/null +++ b/.github/DangerFiles/StaticAnalysis.rb @@ -0,0 +1,42 @@ +require 'plist' + +# Markdown table character length without any issues +MAKRDOWN_LENGTH = 138 +LIBS = ['SalesforceSDKCommon', 'SalesforceAnalytics', 'SalesforceSDKCore', 'SmartStore', 'MobileSync'] + +files = Set[] +for lib in LIBS; + files.merge(Dir["../../libs/#{lib}/clangReport/StaticAnalyzer/#{lib}/#{lib}/normal/**/*.plist"]) +end +print "Found #{files.count} classes with static analysis files.\n" + +modified_file_names = git.modified_files.map { |file| File.basename(file, File.extname(file)) } +added_file_names = git.added_files.map { |file| File.basename(file, File.extname(file)) } + +# Github PR comment header +message = "### Clang Static Analysis Issues\n\n" +message << "File | Type | Category | Description | Line | Col |\n" +message << " --- | ---- | -------- | ----------- | ---- | --- |\n" + +# Parse Clang Plist files and report issues associated with files modified or added in this PR. +for file in files; + report = Plist.parse_xml(file) + report_file_name = File.basename(file, File.extname(file)) + print "File with clang report: #{report_file_name}\n" + + if modified_file_names.include?(report_file_name) || added_file_names.include?(report_file_name) + issues = report['diagnostics'] + print "File modified in PR: #{file}, has #{issue.count} issues.\n" + for i in 0..issues.count-1 + unless issues[i].nil? + message << "#{report_file_name} | #{issues[i]['type']} | #{issues[i]['category']} | #{issues[i]['description']} | #{issues[i]['location']['line']} | #{issues[i]['location']['col']}\n" + end + end + end +end + +# Only print Static Analysis table if there are issues +if message.length > MAKRDOWN_LENGTH + warn('Static Analysis found an issue with one or more files you modified. Please fix the issue(s).') + markdown message +end \ No newline at end of file diff --git a/.github/DangerFiles/TestOrchestrator.rb b/.github/DangerFiles/TestOrchestrator.rb new file mode 100644 index 0000000000..96b28a76ca --- /dev/null +++ b/.github/DangerFiles/TestOrchestrator.rb @@ -0,0 +1,14 @@ +# List of supported xcode schemes for testing +SCHEMES = ['SalesforceSDKCommon', 'SalesforceAnalytics', 'SalesforceSDKCore', 'SmartStore', 'MobileSync'] + +modifed_libs = Set[] +for file in (git.modified_files + git.added_files); + scheme = file.split("libs/").last.split("/").first + if SCHEMES.include?(scheme) + modifed_libs.add(scheme) + end +end + +# Set Github Job output so we know which tests to run +json_libs = modifed_libs.map { |l| "'#{l}'"}.join(", ") +`echo "libs=[#{json_libs}]" >> $GITHUB_OUTPUT` \ No newline at end of file diff --git a/.github/DangerFiles/TestResults.rb b/.github/DangerFiles/TestResults.rb new file mode 100644 index 0000000000..5fb45580c8 --- /dev/null +++ b/.github/DangerFiles/TestResults.rb @@ -0,0 +1,8 @@ +xcode_summary.ignores_warnings = true +xcode_summary.inline_mode = true + +if File.exist?('../../test.xcresult') + xcode_summary.report '../../test.xcresult' +else + fail "No test results found." +end \ No newline at end of file diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml new file mode 100644 index 0000000000..15e13a9d15 --- /dev/null +++ b/.github/workflows/nightly.yaml @@ -0,0 +1,25 @@ +name: Nightly Tests + +on: + schedule: + - cron: "0 5 * * 3,5" # cron is UTC, this translates to 10 PM PST Tues and Thur. + +jobs: + ios-pr: + strategy: + fail-fast: false + matrix: + lib: [SalesforceSDKCommon, SalesforceAnalytics, SalesforceSDKCore, SmartStore, MobileSync] + ios: [^18, ^17] + include: + - ios: ^18 + macos: macos-15 + - ios: ^17 + macos: macos-14 + + uses: ./.github/workflows/reusable-workflow.yaml + with: + lib: ${{ matrix.lib }} + ios: ${{ matrix.ios }} + macos: ${{ matrix.macos }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000000..fd096e3fc0 --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,77 @@ +name: Pull Request + +on: + # Dangerious without Member Check setep! + pull_request_target: + branches: + - dev + +jobs: + static-analysis: + runs-on: macos-15 + steps: + - name: Member Check + if: ${{ github.event.pull_request.author_association != 'MEMBER' && github.event.pull_request.author_association != 'OWNER' }} + run: | + echo "Pull Request not triggered by a MSDK team member. Someone from the team needs to rerun this workflow AFTER it has been deemed safe." + exit 1 + - name: Checkout + uses: actions/checkout@v4 + with: + # We need a sufficient depth or Danger will occasionally run into issues checking which files were modified. + fetch-depth: 100 + # This is dangerous without the member check + ref: ${{ github.event.pull_request.head.sha }} + - name: Install Dependencies + run: | + npm install shelljs@0.8.5 + ./install.sh + - name: Run Static Analysis + # It would be nice to use xcbeaufity here but all the warnings and errors get annotated onto the PR, including + # files not mofified in the PR which is annoying. + run: xcodebuild analyze -workspace SalesforceMobileSDK.xcworkspace -scheme MobileSync -sdk 'iphonesimulator' \ + CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR=./clangReport RUN_CLANG_STATIC_ANALYZER=YES + - name: Report Static Analysis + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd .github/DangerFiles + bundle update && bundle install + bundle exec danger --dangerfile=StaticAnalysis.rb --danger_id=StaticAnalysis + + test-orchestrator: + runs-on: macos-15 + outputs: + libs: ${{ steps.test-orchestrator.outputs.libs }} + steps: + - name: Member Check + if: ${{ github.event.pull_request.author_association != 'MEMBER' }} + run: | + echo "Pull Request not triggered by a MSDK team member. Someone from the team needs to rerun this workflow AFTER it has been deemed safe." + exit 1 + - name: Checkout + uses: actions/checkout@v4 + with: + # We need a sufficient depth or Danger will occasionally run into issues checking which files were modified. + fetch-depth: 100 + # This is dangerous without the member check + ref: ${{ github.event.pull_request.head.sha }} + - name: Determine Tests to Run + id: test-orchestrator + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd .github/DangerFiles + bundle update && bundle install + bundle exec danger --dangerfile=TestOrchestrator.rb + + ios-pr: + needs: [test-orchestrator] + strategy: + fail-fast: false + matrix: + lib: ${{ fromJson(needs.test-orchestrator.outputs.libs) }} + uses: ./.github/workflows/reusable-workflow.yaml + with: + lib: ${{ matrix.lib }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/reusable-workflow.yaml b/.github/workflows/reusable-workflow.yaml new file mode 100644 index 0000000000..c543cbb85d --- /dev/null +++ b/.github/workflows/reusable-workflow.yaml @@ -0,0 +1,60 @@ +on: + workflow_dispatch: + workflow_call: + inputs: + lib: + required: true + type: string + ios: + default: '^18' + required: false + type: string + xcode: + default: '^16' + required: false + type: string + macos: + default: macos-15 + required: false + type: string + +jobs: + test-ios: + runs-on: ${{ inputs.macos }} + steps: + - uses: actions/checkout@v4 + # We need a sufficient depth or Danger will occasionally run into issues + # checking which files were modified. + with: + fetch-depth: 100 + - name: Install Dependencies + env: + TEST_CREDENTIALS: ${{ secrets.TEST_CREDENTIALS }} + run: | + npm install shelljs@0.8.5 + ./install.sh + echo $TEST_CREDENTIALS > ./shared/test/test_credentials.json + - uses: mxcl/xcodebuild@v3 + with: + xcode: ${{ inputs.xcode }} + platform: iOS + platform-version: ${{ inputs.ios }} + workspace: SalesforceMobileSDK.xcworkspace + scheme: ${{ inputs.lib }} + code-coverage: true + upload-logs: always + verbosity: xcbeautify + - name: Danger Test Results + if: (github.event_name == 'pull_request') && failure() + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + cd .github/DangerFiles + bundle update && bundle install + bundle exec danger --dangerfile=TestResults.rb --danger_id=${{ inputs.lib }} + - uses: codecov/codecov-action@v4 + with: + flags: ${{ inputs.lib }} + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + if: success() || failure() \ No newline at end of file diff --git a/.gitignore b/.gitignore index d84a467e08..9223f072bf 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ shared/test/test_credentials.json /libs/MobileSync/build/ /native/SampleApps/RestAPIExplorer/build/ node_modules/ -.github/ .idea/ package-lock.json npm-debug.log