diff --git a/.github/workflows/android-apk.yml b/.github/workflows/android-apk.yml index f28ff67..c477b49 100644 --- a/.github/workflows/android-apk.yml +++ b/.github/workflows/android-apk.yml @@ -6,7 +6,7 @@ on: release_tag: description: GitHub Release tag to upload the APK to required: false - default: v0.1.76 + default: v0.1.77 push: tags: - 'v*' @@ -22,6 +22,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Java uses: actions/setup-java@v5 with: @@ -73,42 +76,22 @@ jobs: - name: Build release APK working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} - MOBILECODE_DEEPSEEK_API_KEY: ${{ secrets.MOBILECODE_DEEPSEEK_API_KEY || secrets.DEEPSEEK_API_KEY }} MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} - MOBILECODE_MANAGED_RELAY_TOKEN: ${{ secrets.MOBILECODE_MANAGED_RELAY_TOKEN }} - MOBILECODE_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.MOBILECODE_GITHUB_OAUTH_CLIENT_ID }} - MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET: ${{ secrets.MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET }} + MOBILECODE_GITHUB_OAUTH_CLIENT_ID: ${{ vars.MOBILECODE_GITHUB_OAUTH_CLIENT_ID }} MOBILECODE_GITHUB_OAUTH_REDIRECT_URI: ${{ vars.MOBILECODE_GITHUB_OAUTH_REDIRECT_URI }} run: | DART_DEFINES=() if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") - fi - if [ -n "$MOBILECODE_MANAGED_RELAY_TOKEN" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_TOKEN="$MOBILECODE_MANAGED_RELAY_TOKEN") - fi - if [ -n "$MOBILECODE_MANAGED_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) - fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY") - fi - if [ -n "$MOBILECODE_DEEPSEEK_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_BASE_URL=https://api.deepseek.com) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_MODEL=deepseek-v4-flash) fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_DEEPSEEK_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_API_KEY="$MOBILECODE_DEEPSEEK_API_KEY") - fi if [ -n "$MOBILECODE_GITHUB_OAUTH_CLIENT_ID" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_CLIENT_ID="$MOBILECODE_GITHUB_OAUTH_CLIENT_ID") - fi - if [ -n "$MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET="$MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET") fi if [ -n "$MOBILECODE_GITHUB_OAUTH_REDIRECT_URI" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_REDIRECT_URI="$MOBILECODE_GITHUB_OAUTH_REDIRECT_URI") @@ -118,7 +101,7 @@ jobs: - name: Stage APK id: stage_apk env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | mkdir -p artifacts APK_PATH="artifacts/mobilecode-${RELEASE_TAG}.apk" @@ -127,7 +110,7 @@ jobs: - name: Verify release version consistency env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | python3 scripts/verify_mobilecode_release_version.py \ --apk "${{ steps.stage_apk.outputs.apk_path }}" \ @@ -142,7 +125,7 @@ jobs: - name: Ensure GitHub Release exists env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -158,6 +141,6 @@ jobs: - name: Upload APK to GitHub Release env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | gh release upload "$RELEASE_TAG" "${{ steps.stage_apk.outputs.apk_path }}" --clobber --repo "$GITHUB_REPOSITORY" diff --git a/.github/workflows/ios-archive.yml b/.github/workflows/ios-archive.yml index d868088..8197b0a 100644 --- a/.github/workflows/ios-archive.yml +++ b/.github/workflows/ios-archive.yml @@ -6,7 +6,7 @@ on: release_tag: description: GitHub Release tag to upload the unsigned iOS archive to required: false - default: v0.1.76 + default: v0.1.77 upload_to_release: description: Upload archive zip to the GitHub Release required: false @@ -17,7 +17,7 @@ permissions: contents: write concurrency: - group: ios-archive-${{ github.ref }}-${{ github.event.inputs.release_tag || 'v0.1.76' }} + group: ios-archive-${{ github.ref }}-${{ github.event.inputs.release_tag || 'v0.1.77' }} cancel-in-progress: false jobs: @@ -29,6 +29,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -60,22 +63,21 @@ jobs: - name: Build unsigned iOS device app working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} + MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} run: | - if [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - flutter build ios --release --no-codesign --target lib/main.dart \ - --dart-define=MOBILECODE_MANAGED_PROVIDER=true \ - --dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic \ - --dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro \ - --dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY" - else - flutter build ios --release --no-codesign --target lib/main.dart + DART_DEFINES=() + if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) fi + flutter build ios --release --no-codesign --target lib/main.dart "${DART_DEFINES[@]}" - name: Package unsigned xcarchive working-directory: mobile_agent env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | set -euxo pipefail mkdir -p ../artifacts @@ -166,7 +168,7 @@ jobs: if: ${{ github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -183,6 +185,6 @@ jobs: if: ${{ github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | gh release upload "$RELEASE_TAG" artifacts/mobilecode-ios-archive-"$RELEASE_TAG".xcarchive.zip artifacts/ios-archive-summary.txt --clobber --repo "$GITHUB_REPOSITORY" diff --git a/.github/workflows/ios-simulator.yml b/.github/workflows/ios-simulator.yml index 786347e..04af0de 100644 --- a/.github/workflows/ios-simulator.yml +++ b/.github/workflows/ios-simulator.yml @@ -6,7 +6,7 @@ on: release_tag: description: GitHub Release tag to upload the iOS simulator build to required: false - default: v0.1.76 + default: v0.1.77 upload_to_release: description: Upload simulator zip to the GitHub Release required: false @@ -22,7 +22,7 @@ permissions: contents: write concurrency: - group: ios-simulator-${{ github.ref }}-${{ github.event.inputs.release_tag || 'v0.1.76' }} + group: ios-simulator-${{ github.ref }}-${{ github.event.inputs.release_tag || 'v0.1.77' }} cancel-in-progress: false jobs: @@ -34,6 +34,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -65,22 +68,21 @@ jobs: - name: Build iOS simulator app working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} + MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} run: | - if [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - flutter build ios --simulator --debug --target lib/main.dart \ - --dart-define=MOBILECODE_MANAGED_PROVIDER=true \ - --dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic \ - --dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro \ - --dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY" - else - flutter build ios --simulator --debug --target lib/main.dart + DART_DEFINES=() + if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) fi + flutter build ios --simulator --debug --target lib/main.dart "${DART_DEFINES[@]}" - name: Install and launch on iOS simulator working-directory: mobile_agent env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | set -euxo pipefail mkdir -p ../artifacts @@ -127,7 +129,7 @@ jobs: if: ${{ github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -150,6 +152,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -175,22 +180,21 @@ jobs: - name: Build unsigned iOS device app working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} + MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} run: | - if [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - flutter build ios --release --no-codesign --target lib/main.dart \ - --dart-define=MOBILECODE_MANAGED_PROVIDER=true \ - --dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic \ - --dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro \ - --dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY" - else - flutter build ios --release --no-codesign --target lib/main.dart + DART_DEFINES=() + if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) + DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) fi + flutter build ios --release --no-codesign --target lib/main.dart "${DART_DEFINES[@]}" - name: Package unsigned xcarchive working-directory: mobile_agent env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | set -euxo pipefail mkdir -p ../artifacts @@ -281,7 +285,7 @@ jobs: if: ${{ github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." diff --git a/.github/workflows/mobile-app-release.yml b/.github/workflows/mobile-app-release.yml index d18d51a..a75a828 100644 --- a/.github/workflows/mobile-app-release.yml +++ b/.github/workflows/mobile-app-release.yml @@ -6,7 +6,7 @@ on: release_tag: description: GitHub Release tag for Android and iOS app artifacts required: false - default: v0.1.76 + default: v0.1.77 upload_to_release: description: Upload app artifacts to the GitHub Release required: false @@ -20,7 +20,7 @@ permissions: contents: write concurrency: - group: mobile-app-release-${{ github.ref }}-${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + group: mobile-app-release-${{ github.ref }}-${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} cancel-in-progress: false jobs: @@ -33,6 +33,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Java uses: actions/setup-java@v5 with: @@ -105,43 +108,23 @@ jobs: - name: Build release APK working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} - MOBILECODE_DEEPSEEK_API_KEY: ${{ secrets.MOBILECODE_DEEPSEEK_API_KEY || secrets.DEEPSEEK_API_KEY }} MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} - MOBILECODE_MANAGED_RELAY_TOKEN: ${{ secrets.MOBILECODE_MANAGED_RELAY_TOKEN }} - MOBILECODE_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.MOBILECODE_GITHUB_OAUTH_CLIENT_ID }} - MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET: ${{ secrets.MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET }} + MOBILECODE_GITHUB_OAUTH_CLIENT_ID: ${{ vars.MOBILECODE_GITHUB_OAUTH_CLIENT_ID }} MOBILECODE_GITHUB_OAUTH_REDIRECT_URI: ${{ vars.MOBILECODE_GITHUB_OAUTH_REDIRECT_URI }} run: | DART_DEFINES=() if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") - fi - if [ -n "$MOBILECODE_MANAGED_RELAY_TOKEN" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_TOKEN="$MOBILECODE_MANAGED_RELAY_TOKEN") - fi - if [ -n "$MOBILECODE_MANAGED_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) - fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY") - fi - if [ -n "$MOBILECODE_DEEPSEEK_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_BASE_URL=https://api.deepseek.com) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_MODEL=deepseek-v4-flash) fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_DEEPSEEK_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_DEEPSEEK_API_KEY="$MOBILECODE_DEEPSEEK_API_KEY") - fi if [ -n "$MOBILECODE_GITHUB_OAUTH_CLIENT_ID" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_CLIENT_ID="$MOBILECODE_GITHUB_OAUTH_CLIENT_ID") fi - if [ -n "$MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET="$MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET") - fi if [ -n "$MOBILECODE_GITHUB_OAUTH_REDIRECT_URI" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_GITHUB_OAUTH_REDIRECT_URI="$MOBILECODE_GITHUB_OAUTH_REDIRECT_URI") fi @@ -150,7 +133,7 @@ jobs: - name: Stage APK id: stage_apk env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | mkdir -p artifacts APK_PATH="artifacts/mobilecode-${RELEASE_TAG}.apk" @@ -159,7 +142,7 @@ jobs: - name: Verify release version consistency env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | python3 scripts/verify_mobilecode_release_version.py \ --apk "${{ steps.stage_apk.outputs.apk_path }}" \ @@ -175,7 +158,7 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -192,7 +175,7 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | gh release upload "$RELEASE_TAG" "${{ steps.stage_apk.outputs.apk_path }}" --clobber --repo "$GITHUB_REPOSITORY" @@ -205,6 +188,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -250,31 +236,21 @@ jobs: - name: Build iOS simulator app working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} - MOBILECODE_MANAGED_RELAY_TOKEN: ${{ secrets.MOBILECODE_MANAGED_RELAY_TOKEN }} run: | DART_DEFINES=() if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") - fi - if [ -n "$MOBILECODE_MANAGED_RELAY_TOKEN" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_TOKEN="$MOBILECODE_MANAGED_RELAY_TOKEN") - fi - if [ -n "$MOBILECODE_MANAGED_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY") - fi flutter build ios --simulator --debug --target lib/main.dart "${DART_DEFINES[@]}" - name: Install and launch on iOS simulator working-directory: mobile_agent env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | set -euxo pipefail mkdir -p ../artifacts @@ -321,7 +297,7 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -338,7 +314,7 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | gh release upload "$RELEASE_TAG" artifacts/mobilecode-ios-simulator-"$RELEASE_TAG".zip artifacts/mobilecode-ios-smoke.png artifacts/ios-runner.log --clobber --repo "$GITHUB_REPOSITORY" @@ -351,6 +327,9 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Enforce public release credential policy + run: python3 scripts/verify_public_release_workflows.py + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -378,31 +357,21 @@ jobs: - name: Build unsigned iOS device app working-directory: mobile_agent env: - MOBILECODE_MANAGED_API_KEY: ${{ secrets.MOBILECODE_MANAGED_API_KEY }} MOBILECODE_MANAGED_RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }} - MOBILECODE_MANAGED_RELAY_TOKEN: ${{ secrets.MOBILECODE_MANAGED_RELAY_TOKEN }} run: | DART_DEFINES=() if [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_URL="$MOBILECODE_MANAGED_RELAY_URL") - fi - if [ -n "$MOBILECODE_MANAGED_RELAY_TOKEN" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_RELAY_TOKEN="$MOBILECODE_MANAGED_RELAY_TOKEN") - fi - if [ -n "$MOBILECODE_MANAGED_API_KEY" ] || [ -n "$MOBILECODE_MANAGED_RELAY_URL" ]; then DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_PROVIDER=true) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_BASE_URL=https://token-plan-cn.xiaomimimo.com/anthropic) DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_MODEL=mimo-v2.5-pro) fi - if [ -z "$MOBILECODE_MANAGED_RELAY_URL" ] && [ -n "$MOBILECODE_MANAGED_API_KEY" ]; then - DART_DEFINES+=(--dart-define=MOBILECODE_MANAGED_API_KEY="$MOBILECODE_MANAGED_API_KEY") - fi flutter build ios --release --no-codesign --target lib/main.dart "${DART_DEFINES[@]}" - name: Package unsigned xcarchive working-directory: mobile_agent env: - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | set -euxo pipefail mkdir -p ../artifacts @@ -493,7 +462,7 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "Release $RELEASE_TAG already exists." @@ -510,6 +479,6 @@ jobs: if: ${{ github.event_name == 'push' || github.event.inputs.upload_to_release != 'false' }} env: GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.76' }} + RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name || 'v0.1.77' }} run: | gh release upload "$RELEASE_TAG" artifacts/mobilecode-ios-archive-"$RELEASE_TAG".xcarchive.zip artifacts/ios-archive-summary.txt --clobber --repo "$GITHUB_REPOSITORY" diff --git a/README.md b/README.md index f75314d..9e7039c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Mobile App Release Android APK Android Smoke - Version + Version Platform

@@ -28,7 +28,7 @@ · HTML Principle Video · - Download v0.1.76 app + Download v0.1.77 app · GitHub Pages Demo

@@ -415,13 +415,14 @@ That keeps the phone lightweight while still letting users produce shareable web ## Release Line -Current candidate: `v0.1.76` (`0.1.76+66`). +Current candidate: `v0.1.77` (`0.1.77+67`). See: - [MobileCore dual-app evidence](docs/mobilecore-dual-app-qa.md) - 30 real offline cross-app requests, buffered/SSE parity, unload/reload, background continuity, low-memory notification, and process restart recovery passed on Android emulator. -- [Release assets](https://github.com/Harzva/mobilecode/releases/tag/v0.1.76) - Android `pure` APK and CI-generated platform evidence for the protocol-gated MobileCore local inference candidate. -- [Signed Android v0.1.76 workflow](https://github.com/Harzva/mobilecode/actions/runs/31128209900) - the downloaded official `0.1.76+66` APK has SHA-256 `52c53c26d51d6335588a443fd3f84f9a36ed9ac093de79a4238a23b2ff3ead31`, verifies with the MobileCode release certificate, and completed a real TuiMa Local `OK` request after a clean emulator install. +- [Release assets](https://github.com/Harzva/mobilecode/releases/tag/v0.1.77) - Android `pure` APK and CI-generated platform evidence for the credential-safe protocol-gated MobileCore local inference candidate. +- v0.1.77 makes the public release workflows fail closed if they reference or compile raw provider keys, relay bearer tokens, or an OAuth client secret. Public relay URLs, OAuth client IDs, and redirect URIs remain allowed configuration; users may still save their own provider key through the app's secure-storage flow. +- The former v0.1.76 official APK was withdrawn after post-build review found that the old public workflow supplied runtime service credentials as Dart compile definitions. The explicitly named debug-signed QA APK remains emulator evidence only; affected provider credentials should be rotated outside the repository. - v0.1.76 requires the `mobilecore.local` v2 compatibility handshake before local model control or inference. Missing, malformed, or unsupported protocols fail closed with typed evidence; MobileCore still cannot perform Phone Use actions. - Local timeouts and explicit Agent pauses now request native inference cancellation, while measured slow runtimes receive a bounded next-response budget; overlapping MobileCore work fails as `runtime_busy` instead of racing the shared llama context. - [Signed Android v0.1.75 workflow](https://github.com/Harzva/mobilecode/actions/runs/31127234312) - the previous downloaded `0.1.75+65` APK has SHA-256 `66e7a26bb7efa4b3c6f959b3e8063fb05a25f91e5b13463211c80c60da5272e2`, verifies with the MobileCode release certificate, and clean-launches on the Android 16 ARM64 emulator without crash, ANR, or OOM. diff --git a/docs/mobilecode-update.json b/docs/mobilecode-update.json index 6eab8a9..51f09a7 100644 --- a/docs/mobilecode-update.json +++ b/docs/mobilecode-update.json @@ -1,17 +1,17 @@ { "schemaVersion": 1, "channel": "prerelease", - "title": "MobileCode v0.1.76 本地协议联动预览", - "message": "本版在保留单任务云端审批边界的同时,在模型控制和本地推理前强制校验 MobileCoreClient v2 协议,并支持超时取消与慢速输出预算。", - "latestVersion": "v0.1.76", - "latestBuildNumber": 66, + "title": "MobileCode v0.1.77 安全联动预览", + "message": "本版保留 MobileCoreClient v2 联动能力,同时禁止公开构建内嵌运行时服务凭据,仅允许公开 relay 与 OAuth 配置。", + "latestVersion": "v0.1.77", + "latestBuildNumber": 67, "minimumSupportedBuildNumber": 58, "severity": "info", - "publishedAt": "2026-08-07T03:45:00+08:00", + "publishedAt": "2026-08-07T05:46:40+08:00", "pagesUrl": "https://harzva.github.io/mobilecode/", "githubUrl": "https://github.com/Harzva/mobilecode", - "releaseUrl": "https://github.com/Harzva/mobilecode/releases/tag/v0.1.76", - "downloadUrl": "https://github.com/Harzva/mobilecode/releases/download/v0.1.76/mobilecode-v0.1.76.apk", + "releaseUrl": "https://github.com/Harzva/mobilecode/releases/tag/v0.1.77", + "downloadUrl": "https://github.com/Harzva/mobilecode/releases/download/v0.1.77/mobilecode-v0.1.77.apk", "ctaLabel": "打开 GitHub Pages", "secondaryCtaLabel": "下载最新构建", "releaseNotes": [ @@ -25,6 +25,8 @@ "端侧输出预算会参考最近一次实测解码速度,在极慢或资源受限环境自动收紧。", "MobileCode 仍以原子快照读取 MobileCore 模型、能力、projector、preflight 与指标状态。", "离线、隐私、热压力和内存压力策略会在云端传输前完成路由判断。", - "Android 模拟器已验证协议握手、真实本地文本请求、取消、并发保护和跨应用图像链;实体机门槛仍单独保留。" + "Android 模拟器已验证协议握手、真实本地文本请求、取消、并发保护和跨应用图像链;实体机门槛仍单独保留。", + "公开 Android/iOS 构建不再接收或编译 Provider API Key、relay token 与 OAuth client secret。", + "CI 会在构建前执行凭据策略检查;仅公开 relay URL、OAuth client ID 与 redirect URI 可进入发布配置。" ] } diff --git a/docs/mobilecode-version-policy.md b/docs/mobilecode-version-policy.md index f7499fd..ae38f95 100644 --- a/docs/mobilecode-version-policy.md +++ b/docs/mobilecode-version-policy.md @@ -4,7 +4,7 @@ MobileCode uses semantic versioning, but the project is still pre-1.0. The version number should communicate release intent clearly, not simply increase because work happened. -Current release line: `0.1.76+66`. +Current release line: `0.1.77+67`. ## Version Lines @@ -63,6 +63,7 @@ Examples: - `0.1.74+64`: coherent MobileCoreClient v2 snapshots and model controls plus task-aware, pressure-aware, proactive offline routing. - `0.1.75+65`: one-task cloud inference consent, fail-closed MobileCore decline routing, and redacted approval evidence. - `0.1.76+66`: strict `mobilecore.local` v2 handshake, adaptive local output budgets, and timeout/user-pause inference cancellation with typed failures. +- `0.1.77+67`: public release workflows reject raw runtime credentials and allow only non-secret relay/OAuth configuration in distributed app artifacts. - `0.2.0+38`: Helper APK/runtime capability expansion starts. ## Stop Rules diff --git a/docs/releases/v0.1.76.md b/docs/releases/v0.1.76.md index 15157a1..00ba9ba 100644 --- a/docs/releases/v0.1.76.md +++ b/docs/releases/v0.1.76.md @@ -2,6 +2,13 @@ Release type: pre-release patch. +> Distribution notice (2026-08-07): the official `mobilecode-v0.1.76.apk` +> asset was withdrawn after post-build review found that the old public build +> workflow supplied runtime service credentials as Dart compile definitions. +> The remaining explicitly named debug-signed APK is QA-only. No credential +> value is recorded here; affected provider credentials should be rotated at +> their providers. Use v0.1.77 or later for public distribution. + ## Scope - Require a `mobilecore.local` v2 protocol handshake before MobileCore model control or inference. diff --git a/docs/releases/v0.1.77.md b/docs/releases/v0.1.77.md new file mode 100644 index 0000000..fc6bd47 --- /dev/null +++ b/docs/releases/v0.1.77.md @@ -0,0 +1,20 @@ +# MobileCode v0.1.77 + +Release type: security hardening pre-release patch. + +## Scope + +- Remove raw managed-provider API keys, DeepSeek keys, relay bearer tokens, and OAuth client secrets from every public Android and iOS build workflow. +- Allow only non-secret public configuration in distributed binaries: managed relay URL, OAuth client ID, and OAuth redirect URI. +- Enable managed MiMo/DeepSeek presets in public builds only when a relay URL is configured; otherwise users provide their own key through MobileCode's secure-storage flow. +- Add a fail-closed repository policy check to every public build job so a later workflow edit cannot silently restore forbidden credential inputs. +- Preserve the MobileCore v2 handshake, model controls, adaptive local routing, attachment capability gates, cancellation, and redacted ActionEvidence behavior from v0.1.76. +- Keep MobileCore as the local inference engine and MobileCode as the orchestration, Phone Use, approval, and evidence authority. + +## Acceptance gates + +- Policy unit tests reject forbidden secret references and runtime-key Dart definitions while allowing release-signing secrets and public configuration. +- All public release workflows pass the credential policy and YAML parsing checks. +- A clean `pureRelease` APK reports `0.1.77+67`, contains no recognizable managed-provider credential values, and verifies with the MobileCode release certificate before publication. +- The exact published APK installs and cold-launches on the Android emulator and completes a controlled MobileCore local request. +- Emulator evidence does not satisfy physical-device thermal, background-recovery, controlled-account, or verified Omni audio gates. diff --git a/mobile_agent/lib/core/mobilecode_version.dart b/mobile_agent/lib/core/mobilecode_version.dart index d7debd5..436c256 100644 --- a/mobile_agent/lib/core/mobilecode_version.dart +++ b/mobile_agent/lib/core/mobilecode_version.dart @@ -1,8 +1,8 @@ class MobileCodeVersion { const MobileCodeVersion._(); - static const String semantic = '0.1.76'; - static const int buildNumber = 66; + static const String semantic = '0.1.77'; + static const int buildNumber = 67; static const String tag = 'v$semantic'; static const String display = tag; static const String githubRepoUrl = 'https://github.com/Harzva/mobilecode'; diff --git a/mobile_agent/pubspec.yaml b/mobile_agent/pubspec.yaml index fec42c7..79fbc83 100644 --- a/mobile_agent/pubspec.yaml +++ b/mobile_agent/pubspec.yaml @@ -2,7 +2,7 @@ name: mobile_agent description: "Mobile Agent - A lightweight Vibing Coding AI companion for mobile devices" publish_to: 'none' -version: 0.1.76+66 +version: 0.1.77+67 environment: sdk: ^3.6.0 diff --git a/scripts/test_verify_public_release_workflows.py b/scripts/test_verify_public_release_workflows.py new file mode 100644 index 0000000..776d59d --- /dev/null +++ b/scripts/test_verify_public_release_workflows.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import sys +import tempfile +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from verify_public_release_workflows import find_violations, verify + + +class PublicReleaseWorkflowPolicyTest(unittest.TestCase): + def test_rejects_secret_reference_and_runtime_key_dart_define(self) -> None: + content = "\n".join( + ( + "TOKEN: ${{ secrets.MOBILECODE_MANAGED_API_KEY }}", + 'run: flutter build apk --dart-define=MOBILECODE_MANAGED_API_KEY="$TOKEN"', + ) + ) + + violations = find_violations(Path("release.yml"), content) + + self.assertEqual(2, len(violations)) + self.assertTrue(all("forbidden public-build credential input" in item for item in violations)) + + def test_rejects_opaque_dart_define_files(self) -> None: + content = "run: flutter build apk --dart-define-from-file=release.json" + + violations = find_violations(Path("release.yml"), content) + + self.assertEqual(1, len(violations)) + self.assertIn("--dart-define-from-file", violations[0]) + + def test_allows_signing_secrets_and_public_configuration(self) -> None: + content = "\n".join( + ( + "KEYSTORE: ${{ secrets.MOBILECODE_RELEASE_KEYSTORE_BASE64 }}", + "RELAY_URL: ${{ vars.MOBILECODE_MANAGED_RELAY_URL }}", + "CLIENT_ID: ${{ vars.MOBILECODE_GITHUB_OAUTH_CLIENT_ID }}", + "REDIRECT_URI: ${{ vars.MOBILECODE_GITHUB_OAUTH_REDIRECT_URI }}", + ) + ) + + self.assertEqual([], find_violations(Path("release.yml"), content)) + + def test_missing_workflow_fails_closed(self) -> None: + with tempfile.TemporaryDirectory() as temporary_directory: + violations = verify(Path(temporary_directory), (Path("missing.yml"),)) + + self.assertEqual(["missing.yml: required public release workflow is missing"], violations) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/verify_public_release_workflows.py b/scripts/verify_public_release_workflows.py new file mode 100644 index 0000000..c48ae1f --- /dev/null +++ b/scripts/verify_public_release_workflows.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Fail when public mobile release workflows embed runtime credentials.""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +PUBLIC_WORKFLOWS = ( + Path(".github/workflows/android-apk.yml"), + Path(".github/workflows/mobile-app-release.yml"), + Path(".github/workflows/ios-archive.yml"), + Path(".github/workflows/ios-simulator.yml"), +) + +FORBIDDEN_PATTERNS = ( + "MOBILECODE_MANAGED_API_KEY", + "MOBILECODE_MANAGED_DEEPSEEK_API_KEY", + "DEEPSEEK_API_KEY", + "MOBILECODE_MANAGED_RELAY_TOKEN", + "MOBILECODE_GITHUB_OAUTH_CLIENT_SECRET", + "--dart-define-from-file", +) + + +def find_violations(path: Path, content: str) -> list[str]: + violations: list[str] = [] + for line_number, line in enumerate(content.splitlines(), start=1): + for pattern in FORBIDDEN_PATTERNS: + if pattern in line: + violations.append(f"{path}:{line_number}: forbidden public-build credential input: {pattern}") + break + return violations + + +def verify(root: Path, workflow_paths: tuple[Path, ...]) -> list[str]: + violations: list[str] = [] + for relative_path in workflow_paths: + path = root / relative_path + if not path.is_file(): + violations.append(f"{relative_path}: required public release workflow is missing") + continue + violations.extend(find_violations(relative_path, path.read_text(encoding="utf-8"))) + return violations + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument("paths", nargs="*", type=Path) + args = parser.parse_args() + workflow_paths = tuple(args.paths) if args.paths else PUBLIC_WORKFLOWS + violations = verify(args.root.resolve(), workflow_paths) + if violations: + for violation in violations: + print(violation) + return 1 + print(f"public release workflow credential policy: OK ({len(workflow_paths)} workflows)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())