Skip to content

feat(MMQA-1951): add new action to download segment analytics contracts - #296

Merged
jvbriones merged 2 commits into
mainfrom
feat/schema-first-approach
Sep 28, 2026
Merged

jvbriones merged 2 commits into
mainfrom
feat/schema-first-approach

Conversation

@jvbriones

@jvbriones jvbriones commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Add reusable action to download analytics contract releases:

  • Select newest published analytics-contracts-* release or an exact tag.
  • Validate required files and SHA-256 asset digest.
  • Expose release metadata and downloaded file paths.

Note

Low Risk
New CI plumbing only; no runtime app or auth logic changes, though callers must grant id-token: write and trust the token exchange flow.

Overview
Adds a reusable composite GitHub Action download-analytics-contract so workflows can pull Segment analytics contract artifacts from a private repo using OIDC (via MetaMask/github-tools/get-token) instead of long-lived secrets.

Callers pass token exchange URL, target repo, asset name, and output directory; an optional release-tag pins a version, otherwise the action picks the latest published analytics-contracts-* release (excluding drafts/prereleases). It resolves release metadata, requires a valid sha256: digest on the asset, downloads manifest.json and the named platform file, verifies the on-disk hash, and exposes tag, URL, digest, and absolute paths as outputs.

Reviewed by Cursor Bugbot for commit d5b478a. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T22:36:12.138235Z bb9dc03 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb9dc03278

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/actions/download-analytics-contract/action.yml

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bb9dc03. Configure here.

Comment thread .github/actions/download-analytics-contract/action.yml Outdated
@jvbriones jvbriones changed the title feat: add new action to download segment analytics contracts feat(MMQA-1951): add new action to download segment analytics contracts Sep 28, 2026
@jvbriones
jvbriones merged commit 68b62dc into main Sep 28, 2026
10 checks passed
@jvbriones
jvbriones deleted the feat/schema-first-approach branch September 28, 2026 10:30
pull Bot pushed a commit to Reality2byte/metamask-mobile that referenced this pull request Sep 29, 2026
…etaMask#36941)

<!--
Please submit this PR as a draft initially.

Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.

In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

This PR introduces the first Mobile-side consumer flow for deployed
typed analytics contracts, focused on CI validation and reproducibility.

It downloads the immutable private Mobile contract through the shared
OIDC action, verifies its metadata and hashes, regenerates the public
`Quick Buy v1/v2 types`, and detects drift in CI.

**Overall changes**

- Download and consumes the deployed Mobile contract through a shared
GitHub Action (github-tools)
- Verifies release metadata, asset digest, semantic contract hash,
source SHA,
  tracking-plan ID, and rule count.
- Adds a pinned contract lock for reproducible local validation.
- Publishes strict Quick Buy Amount Selected v1/v2 TypeScript types.
- Validates that tracked public types match regenerated output in CI.
- Preserves Mobile’s existing analytics runtime and dispatch behavior.
- Adds runtime tests for event-name and version-context forwarding.

- No production analytics call-site migration.
- No changes to `@metamask/analytics-controller`.
- No automatic contract-update PR yet.
- No progressive runtime builder yet.

## Public generated types

The generated Quick Buy facade is tracked publicly at:

- `app/util/analytics/generated/QuickBuyAmountSelected.ts`
- `app/util/analytics/generated/QuickBuyAmountSelected.test-d.ts`

It provides:

- explicit `v1` and `v2` APIs;
- required-property checks;
- enum/value checks;
- unknown-property rejection;
- `context.protocols.event_version`.

The raw contract remains private, i.e. the `metamask-mobile.json`
contract downloaded from segment-schema release is never committed.

## Generator comparison

We compared two approaches against the same immutable Mobile contract
and the
same `Quick Buy Amount Selected` v1/v2 fixture.

- Release: `analytics-contracts-67390efdfde3-8db9e9f80441`
- Contract hash:
`sha256:f75e241c1a832b22c80f9537765e51dac0ba380133f1ec90a6f3e7c77235c4c3`
- Fixture: `Quick Buy Amount Selected` v1/v2

### Lightweight custom generator

The custom generator consumes the verified deployed Mobile contract and
a
reviewed caller-property boundary. It generates:

- separate v1 and v2 property interfaces;
- required and optional property checks;
- enum/string-literal validation;
- unknown-property rejection;
- explicit `context.protocols.event_version`;
- compile-time fixtures for invalid calls;
- a runtime wrapper test for event name and version forwarding.

### Stock Typewriter

The Typewriter comparison used TypeScript with `analytics-react-native`
and a
temporary local `plan.json` derived from the same pinned contract. Its
output
was used only as reference material. **Typewriter was included only for
evaluation and should have been removed before the final merge; it is
not a production dependency or the selected implementation.**

Stock Typewriter:

- retains only the highest version for a repeated event key, so the
fixture
  produces v2 but loses the explicit v1 API;
- emits a permissive `[property: string]: any` index signature;
- does not emit the required event-version context;
- does not provide the progressive builder API;
- does not provide the exact caller-property boundary needed by this
pilot.

### Result

The lightweight generator is the better fit for this Mobile pilot
because it
preserves event versions, enforces exact properties, and supplies the
Segment
event-version context without adding a runtime dependency or requiring
Segment
credentials.

This is not yet a final decision for the all-events generator.
Typewriter’s
schema normalization and generation code may still provide reusable
ideas.
The Typewriter dependency and comparison files are temporary spike
material and
will be removed after review.

The temporary evaluation files are available in the initial spike
commit:
[comparison
spike](MetaMask@f956d52)

The Typewriter comparison was used as a temporary design spike and has
been
removed from the final implementation. The comparison showed that the
narrow
generator is a better fit for this versioned Mobile pilot because it
preserves
v1/v2, rejects unknown properties, and adds event-version context.

## Security and privacy

- The raw contract is never committed.
- GitHub Actions uses short-lived OIDC access through the token exchange
service.
- Local contract and generated comparison output are written only under
ignored
  `temp/`.
- The committed lock contains release identifiers and hashes only.

## Validation

Passed:

- `yarn analytics:contract:check:locked`
- `yarn analytics:contract:compare`
- `yarn jest scripts/analytics/typed-events --runInBand --coverage=false
--forceExit`
- targeted TypeScript check
- targeted ESLint
- Prettier check
- regenerated public facade matches tracked files exactly

Targeted checks passed.

## Follow-up

1. Remove the temporary Typewriter spike files in this PR before merge.
DONE. ✅
2. **After merge**, manually dispatch the workflow from `main`.
3. Confirm OIDC access, contract verification, public-type drift checks,
and
   Slack notification.
4. Open and merge a follow-up PR integrating the generated facade into
one
   production Quick Buy call.
5. After step 4 is merged, add the trusted rolling-update workflow. It
should:
   - detect a newer deployed contract release;
   - update `contract.lock.json`;
   - regenerate the public Quick Buy types;
   - run tests and drift checks;
   - open a reviewable bot PR.
6. Add progressive event construction and runtime completeness
validation.
7. Expand typed coverage to additional event families.


<!-- mms-check: type=text required=true -->

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**
- Refs: https://consensyssoftware.atlassian.net/browse/MMQA-1951
- Refs: Consensys/segment-schema#583
- Refs: MetaMask/github-tools#296

<!-- mms-check: type=issue-link required=true -->

Fixes:

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

In description

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

-  From local, proving the schema contract is downloaded and verified:

<img width="994" height="385" alt="Screenshot 2026-09-29 at 15 42 37"
src="https://github.com/user-attachments/assets/bd8f5eb0-847a-4291-a244-47b549615f13"
/>

- Temporary files dowloaded and generated:

<img width="294" height="162" alt="Screenshot 2026-09-29 at 15 45 19"
src="https://github.com/user-attachments/assets/20bc7439-10e3-419f-937b-f089e4d0c72f"
/>

- The generated public facade exposes separate v1 and v2 property
contracts, including version-specific fields and the required
event-version context (what we have committed in this PR as POC
app/util/analytics/generated/QuickBuyAmountSelected.ts):

<img width="886" height="493" alt="Screenshot 2026-09-29 at 15 53 18"
src="https://github.com/user-attachments/assets/20463bb0-895c-4fad-a6c2-15ad7ada1be6"
/>



### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.

Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> CI/tooling and generated types only; no production analytics path or
runtime behavior changes.
> 
> **Overview**
> Adds **typed analytics contract** validation: Mobile can download the
immutable Segment schema release (`metamask-mobile.json`), verify hashes
and metadata, regenerate a strict TypeScript facade, and fail when
tracked types drift.
> 
> New **`yarn analytics:contract:check`** / **`check:locked`** (plus
`scripts/analytics/typed-events/*`) handle release resolution,
`contract.lock.json`, semantic hash checks, and a narrow generator for
**Quick Buy Amount Selected** v1/v2 with exact properties and
`context.protocols.event_version`. Committed pilot output lives under
`app/util/analytics/generated/` with runtime and compile-time tests.
> 
> A scheduled **`check-analytics-contract`** workflow uses the shared
download action, runs the check, **`diff`s** regenerated files against
the repo, writes a job summary, and posts **Slack** on failure. Docs
describe the lifecycle; **production analytics dispatch is unchanged**
(no call-site migration yet).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0b54ef4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants