Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/actions/feature-flag-drift-slack-noti/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Feature Flag Drift Report
description: 'Sends a Slack notification when feature flag drift is detected in E2E tests vs production.'

inputs:
title:
description: 'Project identifier shown in the message (e.g. MetaMask Extension, MetaMask Mobile)'
required: true
slack-webhook:
description: 'Slack incoming webhook URL'
required: true
workflow-run-url:
description: 'Full URL to the workflow run'
required: true
pr-url:
description: 'URL of the sync PR (optional; included in message when provided)'
required: false
default: ''

runs:
using: composite
steps:
- name: Build Slack payload
id: payload
shell: bash
env:
TITLE: ${{ inputs.title }}
WORKFLOW_URL: ${{ inputs.workflow-run-url }}
PR_URL: ${{ inputs.pr-url }}
run: |
# Use multiple blocks instead of newlines - more reliable in Slack
if [[ -n "$PR_URL" ]]; then
PAYLOAD=$(jq -n \
--arg title "$TITLE" \
--arg pr_url "$PR_URL" \
--arg workflow_url "$WORKFLOW_URL" \
'{
text: ("*[" + $title + "] Feature Flags Drift Detected in E2E tests vs Prod* :warning:"),
blocks: [
{ type: "section", text: { type: "mrkdwn", text: ("*[" + $title + "] Feature Flags Drift Detected in E2E tests vs Prod* :warning:") } },
{ type: "section", text: { type: "mrkdwn", text: "Check the workflow run for details: download the drift report JSON artifact and review the report." } },
{ type: "section", text: { type: "mrkdwn", text: "You can run command `yarn feature-flags:sync:update` locally to update the registry." } },
{ type: "section", text: { type: "mrkdwn", text: ("A sync PR has been created: <" + $pr_url + "|View PR>") } },
{ type: "section", text: { type: "mrkdwn", text: ("<" + $workflow_url + "|View workflow run>") } }
]
}')
else
PAYLOAD=$(jq -n \
--arg title "$TITLE" \
--arg workflow_url "$WORKFLOW_URL" \
'{
text: ("*[" + $title + "] Feature Flags Drift Detected in E2E tests vs Prod* :warning:"),
blocks: [
{ type: "section", text: { type: "mrkdwn", text: ("*[" + $title + "] Feature Flags Drift Detected in E2E tests vs Prod* :warning:") } },
{ type: "section", text: { type: "mrkdwn", text: "Check the workflow run for details: download the drift report JSON artifact and review the report." } },
{ type: "section", text: { type: "mrkdwn", text: "You can run command `yarn feature-flags:sync:update` locally to update the registry." } },
{ type: "section", text: { type: "mrkdwn", text: ("<" + $workflow_url + "|View workflow run>") } }
]
}')
fi
{
echo "payload<<EOF"
printf '%s\n' "$PAYLOAD"
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Send Slack notification
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a
with:
webhook: ${{ inputs.slack-webhook }}
webhook-type: incoming-webhook
payload: ${{ steps.payload.outputs.payload }}
158 changes: 158 additions & 0 deletions .github/workflows/create-pr-feature-flag-registry-drift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
name: Create PR for Feature Flag Registry Drift

on:
workflow_call:
inputs:
repository:
required: true
type: string
description: 'The repository name (e.g. metamask-extension)'
registry-file-path:
required: true
type: string
description: 'Path in the repo where the registry file lives (e.g. app/scripts/feature-flags/registry.json)'
registry-artifact-name:
required: true
type: string
description: 'Name of the artifact containing the updated registry file'
report-artifact-name:
required: true
type: string
description: 'Name of the artifact containing report.json'
pr-label:
required: false
type: string
default: ''
description: 'Label to apply to the created PR (must exist in the target repo)'
workflow-run-url:
required: false
type: string
default: ''
description: 'URL of the workflow run (for artifact download link in PR body)'
secrets:
github-token:
required: true
description: 'Token with contents write and pull-requests write permissions'
outputs:
pr-url:
description: 'URL of the created pull request (empty if no PR was created)'
value: ${{ jobs.create-drift-pr.outputs.pr-url }}
has-changes:
description: 'Whether the registry had drift that needed syncing'
value: ${{ jobs.create-drift-pr.outputs.has-changes }}

jobs:
create-drift-pr:
outputs:
pr-url: ${{ steps.create-pr.outputs.pr-url }}
has-changes: ${{ steps.commit.outputs.has_changes }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
token: ${{ secrets.github-token }}

- name: Download registry artifact
uses: actions/download-artifact@v7
with:
name: ${{ inputs.registry-artifact-name }}
path: ${{ inputs.registry-artifact-name }}

- name: Download report artifact
uses: actions/download-artifact@v7
with:
name: ${{ inputs.report-artifact-name }}
path: ${{ inputs.report-artifact-name }}

- name: Build PR body from report
id: pr-body
env:
REPORT_ARTIFACT: ${{ inputs.report-artifact-name }}
WORKFLOW_RUN_URL: ${{ inputs.workflow-run-url }}
run: |
REPORT_FILE="$REPORT_ARTIFACT/report.json"
if [[ ! -f "$REPORT_FILE" ]]; then
REPORT_FILE=$(find "$REPORT_ARTIFACT" -name "report.json" -type f | head -1)
fi
if [[ -z "$REPORT_FILE" || ! -f "$REPORT_FILE" ]]; then
echo "::error::report.json not found in artifact $REPORT_ARTIFACT"
exit 1
fi
{
echo "## Feature Flag Registry Drift Report"
echo ""
echo "Feature flag drift was detected between E2E tests and production."
echo ""
if [[ -n "$WORKFLOW_RUN_URL" ]]; then
echo "Download the [\`$REPORT_ARTIFACT\` artifact]($WORKFLOW_RUN_URL) for the full report."
else
echo "Download the \`$REPORT_ARTIFACT\` artifact from the workflow run for the full report."
fi
} > pr-body.md

- name: Create branch and commit registry change
id: commit
env:
REPOSITORY: ${{ inputs.repository }}
REGISTRY_ARTIFACT: ${{ inputs.registry-artifact-name }}
REGISTRY_PATH: ${{ inputs.registry-file-path }}
run: |
BRANCH="qa/sync-ff-registry-$REPOSITORY-$(date -u +%Y-%m-%d-%H%M)"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
mkdir -p "$(dirname "$REGISTRY_PATH")"
REGISTRY_SRC=$(find "./$REGISTRY_ARTIFACT" -name "$(basename "$REGISTRY_PATH")" -type f | head -1)
if [[ -z "$REGISTRY_SRC" ]]; then
echo "::error::No registry file found in artifact $REGISTRY_ARTIFACT"
exit 1
fi
cp "$REGISTRY_SRC" "$REGISTRY_PATH"
git add "$REGISTRY_PATH"
git status
if git diff --staged --quiet; then
echo "::warning::No changes to registry file. Nothing to commit."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
git commit -m "chore: sync feature flag registry from E2E"
git push origin "$BRANCH"
{
echo "has_changes=true"
echo "branch=$BRANCH"
echo "timestamp=$(date -u '+%Y-%m-%d %H:%M UTC')"
} >> "$GITHUB_OUTPUT"
fi

- name: Create Pull Request
id: create-pr
if: steps.commit.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.github-token }}
BRANCH: ${{ steps.commit.outputs.branch }}
TIMESTAMP: ${{ steps.commit.outputs.timestamp }}
PR_LABEL: ${{ inputs.pr-label }}
run: |
# Close stale open PRs on qa/sync-ff-registry-* branches (not title search, to avoid closing unrelated PRs)
gh pr list --state open --json number,headRefName 2>/dev/null | jq -r --arg head "$BRANCH" \
'.[] | select(.headRefName | startswith("qa/sync-ff-registry-")) | select(.headRefName != $head) | .number' | \
while read -r pr_number; do
[[ -z "$pr_number" ]] && continue
gh pr close "$pr_number" \
--comment "Superseded by a newer sync PR." \
--delete-branch 2>/dev/null || true
done
LABEL_ARGS=()
if [[ -n "$PR_LABEL" ]]; then
LABEL_ARGS=(--label "$PR_LABEL")
fi
PR_URL=$(gh pr create \
--title "[QA] Sync Feature Flag Registry - $TIMESTAMP" \
--body-file pr-body.md \
--base main \
--head "$BRANCH" \
"${LABEL_ARGS[@]}")
echo "pr-url=$PR_URL" >> "$GITHUB_OUTPUT"
Loading