At least as of of Xcode 11, this can be done very easily and directly with xcodebuild, as part of the export workflow. Just create an exportOptions.plist file that specifies "upload" for the "destination" key and "app-store" for the "method" key. Here's an example, but of course tune to your needs:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>compileBitcode</key>
<true/>
<key>destination</key>
<string>upload</string>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>YOUR_BUNDLE_ID</key>
<string>YOUR_PROFILE_NAME</string>
</dict>
<key>signingCertificate</key>
<string>YOUR_CERT_NAME</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
Once you have that, the command to upload an archive to app store connect is very simple, using the xcodebuild exportArchive command:
xcodebuild -exportArchive \
-archivePath PATH_TO_APP_ARCHIVE \
-exportPath OUTPUT_PATH \
-exportOptionsPlist exportOptions.plist
If you're wondering where your PATH_TO_ARCHIVE is, first just use the xcodebuild archive command, e.g.:
xcodebuild -sdk iphoneos \
-workspace myWorkspace.xcworkspace \
-scheme myScheme \
-configuration Release \
-archivePath PATH_TO_ARCHIVE archive