Repository navigation
chore: Add CI logic to start using typed analytics schema contract #36941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
f956d52
chore(MMQA-1951): Use typed analytics schema contract - Evaluation
jvbriones e2c57d0
chore(MMQA-1951): Use typed analytics schema contract - Evaluation
jvbriones 198cb7e
chore(MMQA-1951): Use typed analytics schema contract - Evaluation
jvbriones 88c8ea2
chore(MMQA-1951): remove Typewriter evaluation
jvbriones 9fbb29b
chore(MMQA-1951): remove Typewriter evaluation
jvbriones a4e9131
chore(MMQA-1951): remove Typewriter evaluation
jvbriones 0b54ef4
chore(MMQA-1951): fix lint error
jvbriones File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| name: Check deployed analytics contract | ||
|
|
||
| # periodically/manual -> download newest deployed analytics contract -> verify it -> | ||
| # generate pilot -> type-check -> notify success/failure -> change nothing. | ||
| # | ||
| # more details in /docs/readme/typed-analytics-contracts.md | ||
|
|
||
| on: | ||
| schedule: | ||
| # Hourly from 06:00 through 22:00 UTC on weekdays. | ||
| - cron: '0 6-22 * * 1-5' | ||
| workflow_dispatch: | ||
| inputs: | ||
| release-tag: | ||
| description: Optional exact analytics contract release tag | ||
| required: false | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| concurrency: | ||
| group: check-deployed-analytics-contract | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| verify-mobile-contract: | ||
| name: Verify latest deployed Mobile contract | ||
| environment: default-branch | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 40 | ||
| outputs: | ||
| release-tag: ${{ steps.shared-contract.outputs.release-tag }} | ||
| release-url: ${{ steps.shared-contract.outputs.release-url }} | ||
| steps: | ||
| - name: Checkout Mobile | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version-file: '.nvmrc' | ||
| cache: yarn | ||
|
|
||
| - name: Install dependencies | ||
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | ||
| with: | ||
| timeout_minutes: 10 | ||
| max_attempts: 3 | ||
| retry_wait_seconds: 30 | ||
| command: yarn install --immutable | ||
|
|
||
| - name: Download contract with shared action | ||
| id: shared-contract | ||
| uses: MetaMask/github-tools/.github/actions/download-analytics-contract@v1 | ||
| with: | ||
| token-exchange-url: ${{ vars.TOKEN_EXCHANGE_URL }} | ||
| target-repository: Consensys/segment-schema | ||
| asset-name: metamask-mobile.json | ||
| release-tag: ${{ inputs.release-tag }} | ||
| output-directory: ${{ runner.temp }}/analytics-contract | ||
|
|
||
| - name: Check downloaded analytics contract | ||
| id: check | ||
| env: | ||
| SHARED_RELEASE_TAG: ${{ steps.shared-contract.outputs.release-tag }} | ||
| SHARED_RELEASE_URL: ${{ steps.shared-contract.outputs.release-url }} | ||
| SHARED_MANIFEST_PATH: ${{ steps.shared-contract.outputs.manifest-path }} | ||
| SHARED_ASSET_PATH: ${{ steps.shared-contract.outputs.asset-path }} | ||
| SHARED_ASSET_DIGEST: ${{ steps.shared-contract.outputs.asset-digest }} | ||
| JSON_OUTPUT: ${{ runner.temp }}/analytics-contract-check.json | ||
| GENERATED_OUTPUT_DIRECTORY: ${{ runner.temp }}/analytics-contract-check | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| yarn analytics:contract:check \ | ||
| --manifest "${SHARED_MANIFEST_PATH}" \ | ||
| --contract "${SHARED_ASSET_PATH}" \ | ||
| --asset-sha256 "${SHARED_ASSET_DIGEST}" \ | ||
| --release-tag "${SHARED_RELEASE_TAG}" \ | ||
| --release-url "${SHARED_RELEASE_URL}" \ | ||
| --output-directory "${GENERATED_OUTPUT_DIRECTORY}" \ | ||
| --json-output "${JSON_OUTPUT}" | ||
| diff -u \ | ||
| app/util/analytics/generated/QuickBuyAmountSelected.ts \ | ||
| "${GENERATED_OUTPUT_DIRECTORY}/generated/QuickBuyAmountSelected.ts" | ||
| diff -u \ | ||
| app/util/analytics/generated/QuickBuyAmountSelected.test-d.ts \ | ||
| "${GENERATED_OUTPUT_DIRECTORY}/generated/QuickBuyAmountSelected.test-d.ts" | ||
| echo "release-tag=$(jq -r '.release.tag' "${JSON_OUTPUT}")" >> "${GITHUB_OUTPUT}" | ||
| echo "release-url=$(jq -r '.release.url' "${JSON_OUTPUT}")" >> "${GITHUB_OUTPUT}" | ||
|
|
||
| - name: Summarize verification | ||
| if: always() | ||
| env: | ||
| RELEASE_TAG: ${{ steps.shared-contract.outputs.release-tag }} | ||
| RELEASE_URL: ${{ steps.shared-contract.outputs.release-url }} | ||
| CHECK_RESULT: ${{ steps.check.outcome }} | ||
| CHECK_FILE: ${{ runner.temp }}/analytics-contract-check.json | ||
| shell: bash | ||
| run: | | ||
| { | ||
| echo "### Deployed Mobile analytics contract" | ||
| echo | ||
| if [ -n "${RELEASE_TAG}" ]; then | ||
| echo "- Release: [\`${RELEASE_TAG}\`](${RELEASE_URL})" | ||
| else | ||
| echo "- Release: unresolved" | ||
| fi | ||
| echo "- Contract, generation, and type-check result: \`${CHECK_RESULT}\`" | ||
|
|
||
| if [ -f "${CHECK_FILE}" ]; then | ||
| echo "- Source SHA: \`$(jq -r '.verification.sourceSha' "${CHECK_FILE}")\`" | ||
| echo "- Rule count: \`$(jq -r '.verification.ruleCount' "${CHECK_FILE}")\`" | ||
| echo "- Contract hash: \`$(jq -r '.verification.contractHash' "${CHECK_FILE}")\`" | ||
| echo "- Checked event versions: \`$(jq -r '.generated.versions | join(", ")' "${CHECK_FILE}")\`" | ||
| fi | ||
| } >> "${GITHUB_STEP_SUMMARY}" | ||
|
|
||
| notify-slack: | ||
| name: Notify Slack | ||
| needs: verify-mobile-contract | ||
| if: >- | ||
| ${{ | ||
| always() && | ||
| !cancelled() | ||
| }} | ||
| environment: default-branch | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_PLAYWRIGHT_HEALTH_REPORT }} | ||
| steps: | ||
| - name: Send Slack notification | ||
| if: env.SLACK_WEBHOOK != '' | ||
| uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a | ||
| with: | ||
| webhook: ${{ env.SLACK_WEBHOOK }} | ||
| webhook-type: incoming-webhook | ||
| payload: | | ||
| { | ||
| "text": "Mobile deployed analytics contract check: ${{ needs.verify-mobile-contract.result }}\nRelease: ${{ needs.verify-mobile-contract.outputs.release-tag || 'unresolved' }}\nRun: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" | ||
| } | ||
41 changes: 41 additions & 0 deletions
41
app/util/analytics/generated/QuickBuyAmountSelected.test-d.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { createQuickBuyAmountSelected } from './QuickBuyAmountSelected'; | ||
|
|
||
| declare const transport: Parameters<typeof createQuickBuyAmountSelected>[0]; | ||
|
|
||
| const event = createQuickBuyAmountSelected(transport); | ||
|
|
||
| event.v1.track({ | ||
| amount_selection_method: 'preset', | ||
| amount_usd: 1, | ||
| source: 'notification', | ||
| }); | ||
|
|
||
| event.v2.track({ | ||
| amount_selection_method: 'preset', | ||
| amount_usd: 1, | ||
| source: 'notification', | ||
| }); | ||
|
|
||
| // @ts-expect-error source is required | ||
| event.v2.track({ | ||
| amount_selection_method: 'custom_input', | ||
| amount_usd: 1, | ||
| }); | ||
|
|
||
| event.v2.track({ | ||
| amount_selection_method: 'preset', | ||
| amount_usd: 1, | ||
| source: 'notification', | ||
| // @ts-expect-error unknown properties are rejected | ||
| unknown_property: true, | ||
| }); | ||
|
|
||
| event.v1.track({ | ||
| // @ts-expect-error slider is not a v1 amount selection method | ||
| amount_selection_method: 'slider', | ||
| amount_usd: 1, | ||
| source: 'notification', | ||
| }); | ||
|
|
||
| // @ts-expect-error only generated versions are available | ||
| event.v3.track({}); |
52 changes: 52 additions & 0 deletions
52
app/util/analytics/generated/QuickBuyAmountSelected.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { createQuickBuyAmountSelected } from './QuickBuyAmountSelected'; | ||
|
|
||
| describe('createQuickBuyAmountSelected', () => { | ||
| it('tracks v1 with the event version context', () => { | ||
| const transport = { | ||
| track: jest.fn(), | ||
| }; | ||
| const event = createQuickBuyAmountSelected(transport); | ||
| const properties = { | ||
| amount_selection_method: 'preset' as const, | ||
| amount_usd: 50, | ||
| source: 'notification' as const, | ||
| }; | ||
|
|
||
| event.v1.track(properties); | ||
|
|
||
| expect(transport.track).toHaveBeenCalledWith( | ||
| 'Quick Buy Amount Selected', | ||
| properties, | ||
| { | ||
| protocols: { | ||
| event_version: 1, | ||
| }, | ||
| }, | ||
| ); | ||
| }); | ||
|
|
||
| it('tracks v2 with the event version context', () => { | ||
| const transport = { | ||
| track: jest.fn(), | ||
| }; | ||
| const event = createQuickBuyAmountSelected(transport); | ||
| const properties = { | ||
| amount_selection_method: 'slider' as const, | ||
| amount_usd: 50, | ||
| slider_percent: 25, | ||
| source: 'trader_feed' as const, | ||
| }; | ||
|
|
||
| event.v2.track(properties); | ||
|
|
||
| expect(transport.track).toHaveBeenCalledWith( | ||
| 'Quick Buy Amount Selected', | ||
| properties, | ||
| { | ||
| protocols: { | ||
| event_version: 2, | ||
| }, | ||
| }, | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| /** | ||
| * GENERATED FILE — DO NOT EDIT. | ||
| * | ||
| * Generated from the verified deployed Mobile analytics contract. | ||
| */ | ||
|
|
||
| export interface AnalyticsEventContext { | ||
| readonly protocols: { | ||
| readonly event_version: number; | ||
| }; | ||
| } | ||
|
|
||
| export interface AnalyticsEventTransport { | ||
| track( | ||
| eventName: string, | ||
| properties: object, | ||
| context: AnalyticsEventContext, | ||
| ): void; | ||
| } | ||
|
|
||
| type ExactProperties<Expected, Actual extends Expected> = Actual & | ||
| Record<Exclude<keyof Actual, keyof Expected>, never>; | ||
|
|
||
| interface EventVersion<Properties> { | ||
| track<Actual extends Properties>( | ||
| properties: ExactProperties<Properties, Actual>, | ||
| ): void; | ||
| } | ||
|
|
||
| const createEventVersion = <Properties>( | ||
| transport: AnalyticsEventTransport, | ||
| eventName: string, | ||
| version: number, | ||
| ): EventVersion<Properties> => ({ | ||
| track: (properties) => { | ||
| transport.track(eventName, properties as object, { | ||
| protocols: { | ||
| event_version: version, | ||
| }, | ||
| }); | ||
| }, | ||
| }); | ||
|
|
||
| const EVENT_NAME = 'Quick Buy Amount Selected'; | ||
|
|
||
| export interface QuickBuyAmountSelectedV1Properties { | ||
| /** How the user picked the amount. */ | ||
| readonly amount_selection_method: 'preset' | 'custom_input'; | ||
| /** Selected USD amount. */ | ||
| readonly amount_usd: number; | ||
| /** CAIP-19 identifier of the token associated with the event. */ | ||
| readonly caip19?: string; | ||
| /** Symbol of the source token currently selected to pay with (may be undefined before token resolution finishes). */ | ||
| readonly pay_with_token?: string; | ||
| /** Preset chip value, when amount_selection_method is preset. */ | ||
| readonly preset_value?: 20 | 50 | 100 | 250; | ||
| /** Surface from which the QuickBuy sheet was opened. */ | ||
| readonly source: | ||
| | 'notification' | ||
| | 'profile_position' | ||
| | 'leaderboard' | ||
| | 'asset_details' | ||
| | 'market_insights' | ||
| | 'security_trust' | ||
| | 'explore_search' | ||
| | 'explore_crypto' | ||
| | 'explore_now' | ||
| | 'explore_rwas' | ||
| | 'explore_trending' | ||
| | 'explore_stocks'; | ||
| /** Wallet address of the trader. May be absent on cold-start notification deeplinks where only a profile UUID is available. */ | ||
| readonly trader_address?: string; | ||
| } | ||
|
|
||
| export interface QuickBuyAmountSelectedV2Properties { | ||
| /** How the user picked the amount. */ | ||
| readonly amount_selection_method: 'preset' | 'custom_input' | 'slider'; | ||
| /** Selected USD amount. */ | ||
| readonly amount_usd: number; | ||
| /** CAIP-19 identifier of the spot token associated with the event, including chain ID. Sent for spot positions only; absent for perps. */ | ||
| readonly caip19?: string; | ||
| /** Lowercase chain slug for the position. Spot examples: base, ethereum, solana. Perps: hyperliquid. Present on follow-trading token events when a position context is resolved. */ | ||
| readonly chain_name?: string; | ||
| /** How the user reached the follow-trading token (trade) screen before opening Quick Buy. Only set when Quick Buy is opened from the trade screen. */ | ||
| readonly original_entry_point?: | ||
| | 'leaderboard' | ||
| | 'trader_profile' | ||
| | 'notification' | ||
| | 'deep_link' | ||
| | 'home_carousel'; | ||
| /** Symbol of the source token currently selected to pay with (may be undefined before token resolution finishes). */ | ||
| readonly pay_with_token?: string; | ||
| /** Tradable Hyperliquid perps market symbol. Sent for perp positions only; absent for spot. */ | ||
| readonly perps_market?: string; | ||
| /** Preset chip value, when amount_selection_method is preset. */ | ||
| readonly preset_value?: 10 | 50 | 100 | 250; | ||
| /** Slider position (0–100), when amount_selection_method is slider. */ | ||
| readonly slider_percent?: number; | ||
| /** Surface from which the QuickBuy sheet was opened. */ | ||
| readonly source: | ||
| | 'notification' | ||
| | 'profile_position' | ||
| | 'leaderboard' | ||
| | 'trader_feed' | ||
| | 'asset_details' | ||
| | 'market_insights' | ||
| | 'security_trust' | ||
| | 'explore_search' | ||
| | 'explore_crypto' | ||
| | 'explore_now' | ||
| | 'explore_rwas' | ||
| | 'explore_trending' | ||
| | 'explore_stocks'; | ||
| /** Wallet address of the trader. May be absent on cold-start notification deeplinks where only a profile UUID is available. */ | ||
| readonly trader_address?: string; | ||
| } | ||
|
|
||
| export const createQuickBuyAmountSelected = ( | ||
| transport: AnalyticsEventTransport, | ||
| ) => ({ | ||
| v1: createEventVersion<QuickBuyAmountSelectedV1Properties>( | ||
| transport, | ||
| EVENT_NAME, | ||
| 1, | ||
| ), | ||
| v2: createEventVersion<QuickBuyAmountSelectedV2Properties>( | ||
| transport, | ||
| EVENT_NAME, | ||
| 2, | ||
| ), | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.