Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d04e7cf
feat(gh-action): create workflow for automatize stable-main creation
alucardzom May 13, 2025
70f94aa
chore: remove unused input
alucardzom May 15, 2025
9dcf8bd
chore: update stable sync
alucardzom May 15, 2025
6fea9a0
chore: added workflow call
alucardzom May 15, 2025
887c99d
chore: pin pr action from v7 to v5
alucardzom May 15, 2025
e7c2d75
chore: comment the Create PR step, to test previous one before
alucardzom May 15, 2025
c9473ee
chore: set user and email
alucardzom May 15, 2025
51937a5
chore: added global as well
alucardzom May 15, 2025
373fb6d
chore: enable create PR on GH toolkit
alucardzom May 16, 2025
9664956
chore: commented peter evans PR create step
alucardzom May 16, 2025
a769465
chore: added CREATE_BRANCH env for the sync step
alucardzom May 16, 2025
cafe053
chore: improve the template
alucardzom May 16, 2025
23607c1
chore: update adding env var to create branch
alucardzom May 16, 2025
3b26c04
chore: add gh command to push the branch
alucardzom May 16, 2025
fcfd2d3
chore: test create with gh command
alucardzom May 16, 2025
3a67a8e
chore: gh auth login
alucardzom May 16, 2025
0279f78
chore: test without using gh
alucardzom May 16, 2025
93207bd
chore: improve body PR
alucardzom May 16, 2025
34c5d2c
chore: update sync script
alucardzom May 16, 2025
84d60c0
chore: execute chantes even if the PR exists
alucardzom May 16, 2025
85aeea3
chore: check for push outside of the box
alucardzom May 16, 2025
fe969ac
chore: added pull before push
alucardzom May 16, 2025
805133a
chore: tes new script changes
alucardzom May 16, 2025
0795ec6
chore: push
alucardzom May 16, 2025
7752444
chore: test
alucardzom May 16, 2025
1cbd044
chore: improve script for mobile and extension
alucardzom May 16, 2025
d543dd0
chore: clean up body message
alucardzom May 16, 2025
68cdc5f
chore: lint yaml
alucardzom May 16, 2025
22dc82d
chore: linter passed
alucardzom May 16, 2025
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
175 changes: 175 additions & 0 deletions .github/scripts/stable-sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env node

// USAGE:
// This will create/update a local stable-sync branch
// and get it in the state needed for a stable-sync PR
// Once the script successfully completes, you just
// need to push the branch to the remote repo. This will
// likely require a `git push --force`
//
// Usage: node stable-sync.js [branch-name]
// If no branch name is provided, defaults to 'stable-sync'
//
// Environment variables:
// CREATE_BRANCH - if set to 'true', will push the branch at the end

const { promisify } = require('util');
const exec = promisify(require('child_process').exec);

async function runGitCommands() {
// Get branch name from command line arguments or use default
const branchName = process.argv[2] || 'stable-main';

// Check if CREATE_BRANCH environment variable exists and is set to true
const shouldPushBranch = (process.env.CREATE_BRANCH || 'false').toLowerCase() === 'true';

try {
try {
// Check if the branch already exists
const { stdout: branchExists } = await exec(
//`git rev-parse --quiet --verify ${branchName}`,
`git ls-remote origin ${branchName}`,
);
if (branchExists.trim()) {
// Branch exists, so simply check it out
await exec(`git checkout ${branchName}`);
await exec(`git pull origin ${branchName}`);
console.log(`Checked out branch: ${branchName}`);
} else {
throw new Error(
'git rev-parse --quiet --verify failed. Branch hash empty',
);
}
} catch (error) {
if (error.stdout === '') {
console.warn(
`Branch does not exist, creating new ${branchName} branch.`,
);

// Branch does not exist, create and check it out
await exec(`git checkout -b ${branchName}`);
console.log(`Created and checked out branch: ${branchName}`);
} else {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}

await exec('git fetch');
console.log('Executed: git fetch');

await exec('git reset --hard origin/stable');
console.log('Executed: git reset --hard origin/stable');

try {
await exec('git merge origin/main');
console.log('Executed: git merge origin/main');
} catch (error) {
// Handle the error but continue script execution
if (
error.stdout.includes(
'Automatic merge failed; fix conflicts and then commit the result.',
)
) {
console.warn(
'Merge conflict encountered. Continuing script execution.',
);
} else {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}

await exec('git add .');
await exec('git restore --source origin/main .');
console.log('Executed: it restore --source origin/main .');

await exec('git checkout origin/main -- .');
console.log('Executed: git checkout origin/main -- .');

await exec('git checkout origin/stable -- CHANGELOG.md');
console.log('Executed: git checkout origin/stable -- CHANGELOG.md');

// Execute mobile-specific commands if REPO is 'mobile'
if (process.env.REPO === 'mobile') {
console.log('Executing mobile-specific commands...');

await exec('git checkout origin/stable -- bitrise.yml');
console.log('Executed: git checkout origin/stable -- bitrise.yml');

await exec('git checkout origin/stable -- android/app/build.gradle');
console.log('Executed: git checkout origin/stable -- android/app/build.gradle');

await exec('git checkout origin/stable -- ios/MetaMask.xcodeproj/project.pbxproj');
console.log('Executed: git checkout origin/stable -- ios/MetaMask.xcodeproj/project.pbxproj');

await exec('git checkout origin/stable -- package.json');
console.log('Executed: git checkout origin/stable -- package.json');
}
// Execute extension-specific commands if REPO is 'extension'
else if (process.env.REPO === 'extension') {
console.log('Executing extension-specific commands...');

const { stdout: packageJsonContent } = await exec(
'git show origin/master:package.json',
);
const packageJson = JSON.parse(packageJsonContent);
const packageVersion = packageJson.version;

await exec(`yarn version "${packageVersion}"`);
console.log('Executed: yarn version');
}
// If REPO is not set or has an invalid value, skip both
else {
console.log('REPO environment variable not set or invalid. Skipping mobile/extension specific commands.');
}

await exec('git add .');
console.log('Executed: git add .');

try {
// Check if there are any changes to commit
const { stdout: status } = await exec('git status --porcelain');
if (!status.trim()) {
console.log('No changes to commit, skipping commit step');
return;
}

await exec(`git commit -m "Merge origin/main into ${branchName}" --no-verify`);
console.log('Executed: git commit');
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}

console.log(`Your local ${branchName} branch is now ready to become a PR.`);

// Push the branch if CREATE_BRANCH is true
if (shouldPushBranch) {
try {
console.log(`Checking if branch ${branchName} exists remotely...`);
const { stdout: remoteBranches } = await exec('git ls-remote --heads origin');
const branchExists = remoteBranches.includes(`refs/heads/${branchName}`);

if (branchExists) {
console.log(`Branch ${branchName} exists remotely, updating...`);
await exec(`git push origin ${branchName}`);
} else {
console.log(`Branch ${branchName} does not exist remotely, creating...`);
await exec(`git push --set-upstream origin ${branchName}`);
}
console.log(`Successfully pushed branch ${branchName} to remote`);
} catch (error) {
console.error(`Error pushing branch: ${error.message}`);
process.exit(1);
}
} else {
console.log('You likely now need to do `git push --force`');
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}

runGitCommands();
114 changes: 114 additions & 0 deletions .github/workflows/stable-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Stable Sync

on:
workflow_dispatch:
inputs:
semver-version:
required: true
type: string
description: 'The semantic version to use for the sync (e.g., x.x.x)'
repo-type:
required: false
type: choice
description: 'Type of repository (mobile or extension)'
options:
- mobile
- extension
default: 'mobile'
workflow_call:
inputs:
semver-version:
required: true
type: string
description: 'The semantic version to use for the sync (e.g., x.x.x)'
repo-type:
required: false
type: string
description: 'Type of repository (mobile or extension)'
default: 'mobile'

jobs:
stable-sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Check if PR exists
Comment thread
makemesteaks marked this conversation as resolved.
id: check-pr
uses: actions/github-script@v7
with:
script: |
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:stable-main-${process.env.SEMVER_VERSION}`,
base: 'main'
});
return prs.length > 0;
env:
SEMVER_VERSION: ${{ inputs.semver-version }}

- name: Set Git user and email
run: |
git config --global user.name "metamaskbot"
git config --global user.email "metamaskbot@users.noreply.github.com"

- name: Run stable sync
id: run-stable-sync
# if: steps.check-pr.outputs.result != 'true'
env:
CREATE_BRANCH: 'false' # let the script handle the branch creation
REPO: ${{ inputs.repo-type }} # Default to 'mobile' if not specified
run: |
node .github/scripts/stable-sync.js "stable-main-${{ inputs.semver-version }}"
# Check if branch exists remotely
BRANCH_NAME="stable-main-${{ inputs.semver-version }}"
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
git pull --rebase
echo "Branch $BRANCH_NAME exists remotely, pushing normally"
git push origin "$BRANCH_NAME" --force
else
echo "Branch $BRANCH_NAME doesn't exist remotely, pushing with --set-upstream"
git push --set-upstream origin "$BRANCH_NAME"
fi

- name: Create Pull Request
if: steps.check-pr.outputs.result != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH_NAME: stable-main-${{ inputs.semver-version }}
VERSION: ${{ inputs.semver-version }}
run: |
# Create PR using GitHub CLI
gh pr create \
--title "chore: sync stable to main for version $VERSION" \
--body "This PR syncs the stable branch to main for version $VERSION.

*Synchronization Process:*

- Fetches the latest changes from the remote repository
- Resets the branch to match the stable branch
- Attempts to merge changes from main into the branch
- Handles merge conflicts if they occur

*File Preservation:*

Preserves specific files from the stable branch:
- CHANGELOG.md
- bitrise.yml
- android/app/build.gradle
- ios/MetaMask.xcodeproj/project.pbxproj
- package.json

Indicates the next version candidate of main to $VERSION" \
--base main \
--head "$BRANCH_NAME"
#--label "sync" \
#--label "stable"
Loading