Files
self/packages/mobile-sdk-alpha/scripts/setup-native-source.cjs
Javier Cortejoso 2a092f5571 chore: Using Namespace.so for MacOS Runners (#1500)
* chore: update CI workflows to include Java setup and modify runner environments

- Added Java installation checks and setup steps to multiple CI workflows to ensure Java is available for builds.
- Changed runner environments for iOS builds from `macos-latest-large` to `namespace-profile-apple-silicon-6cpu` for better resource management.
- Updated push triggers for CI workflows to include specific branches and paths for more controlled execution.

* refactor: streamline AES-GCM encryption tag validation in encryptAES256GCM function

- Removed redundant checks for the AES-GCM authentication tag, simplifying the code while maintaining functionality.
- Updated the return structure to directly convert the authentication tag to a binary format, enhancing clarity and efficiency.

* chore: add Actionlint configuration for custom runner labels

* chore: update mobile deployment workflows for testing configuration

* chore: included a step to set the INSTALL_JAVA environment variable to false

* chore: update logging in setup-native-source script for improved debugging

* chore: simplify mobile CI workflow by removing redundant iOS and Android build steps

- Removed extensive iOS and Android build steps from the mobile CI workflow, as build verification is now handled by the mobile-e2e.yml workflow.

* chore: update mobile workflows to remove push triggers for improved clarity

- Removed push triggers from mobile CI, E2E, and bundle analysis workflows to streamline execution and focus on pull request events.
- This change enhances workflow clarity and reduces unnecessary runs on branch pushes.

* Revert "chore: simplify mobile CI workflow by removing redundant iOS and Android build steps"

This reverts commit 30d5f585c2.

* Updated the conditions for running iOS and Android build jobs to only trigger on workflow dispatch events, reducing unnecessary executions.

* chore: enhance mobile CI workflows with push triggers for improved execution control

- Added push triggers for dev, staging, and main branches in mobile bundle analysis, E2E, and demo E2E workflows to ensure builds are triggered on relevant changes.
- Included conditions for running iOS E2E tests based on issue comments, allowing for more flexible testing workflows.

* Addind workflow_dispatch option

* chore: refine mobile E2E workflows by removing issue comment triggers

- Eliminated issue comment event triggers from mobile E2E workflows to streamline execution and focus on workflow dispatch and push events.
- This change enhances clarity and reduces unnecessary complexity in the CI process.

* chore: remove checkout action from npm publish workflow

- Eliminated the checkout action from the npm publish workflow to streamline the process and reduce unnecessary steps.
- This change aims to enhance the efficiency of the CI pipeline.
2025-12-22 17:15:37 +01:00

278 lines
9.9 KiB
JavaScript

// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
// Constants
const SCRIPT_DIR = __dirname;
const SDK_DIR = path.dirname(SCRIPT_DIR);
const REPO_ROOT = path.resolve(SDK_DIR, '../../');
const PRIVATE_MODULE_PATH = path.join(SDK_DIR, 'mobile-sdk-native');
const GITHUB_ORG = 'selfxyz';
const REPO_NAME = 'mobile-sdk-native';
const BRANCH = 'main';
// Environment detection
const isCI = process.env.CI === 'true';
const repoToken = process.env.SELFXYZ_INTERNAL_REPO_PAT;
const appToken = process.env.SELFXYZ_APP_TOKEN; // GitHub App installation token
const isDryRun = process.env.DRY_RUN === 'true';
function log(message, type = 'info') {
const prefix =
{
info: '🔧',
success: '✅',
warning: '⚠️',
error: '❌',
cleanup: '🗑️',
}[type] || '📝';
console.log(`${prefix} ${message}`);
}
function runCommand(command, options = {}, cwd = SDK_DIR) {
const defaultOptions = {
stdio: isDryRun ? 'pipe' : 'inherit',
cwd: cwd,
encoding: 'utf8',
...options,
};
// Sanitize command for logging to prevent credential exposure
const sanitizedCommand = sanitizeCommandForLogging(command);
try {
if (isDryRun) {
log(`[DRY RUN] Would run: ${sanitizedCommand}`, 'info');
return '';
}
log(`Running: ${sanitizedCommand}`, 'info');
return execSync(command, defaultOptions);
} catch (error) {
log(`Failed to run: ${sanitizedCommand}`, 'error');
log(`Error: ${error.message}`, 'error');
throw error;
}
}
function sanitizeCommandForLogging(command) {
// Replace any https://token@github.com patterns with https://[REDACTED]@github.com
return command.replace(/https:\/\/[^@]+@github\.com/g, 'https://[REDACTED]@github.com');
}
// function removeExistingModule() {
// if (fs.existsSync(PRIVATE_MODULE_PATH)) {
// log(`Removing existing ${REPO_NAME} directory...`, 'cleanup');
// runCommand(`rm -rf "${PRIVATE_MODULE_PATH}"`);
// }
// }
function usingHTTPSGitAuth() {
try {
const result = execSync('git config --get remote.origin.url', {
encoding: 'utf8',
cwd: SDK_DIR,
});
return result.trim().startsWith('https://');
} catch {
log('Could not determine git remote URL, assuming SSH authentication', 'warning');
return false;
}
}
function setupSubmodule() {
log(`Setting up ${REPO_NAME} as submodule...`, 'info');
let submoduleUrl;
if (isCI && appToken) {
// CI environment with GitHub App installation token
// Security: NEVER embed credentials in git URLs. Rely on CI-provided auth via:
// - ~/.netrc, a Git credential helper, or SSH agent configuration.
submoduleUrl = `https://github.com/${GITHUB_ORG}/${REPO_NAME}.git`;
} else if (isCI && repoToken) {
// CI environment with Personal Access Token
// Security: NEVER embed credentials in git URLs. Rely on CI-provided auth via:
// - ~/.netrc, a Git credential helper, or SSH agent configuration.
submoduleUrl = `https://github.com/${GITHUB_ORG}/${REPO_NAME}.git`;
} else if (isCI) {
log('CI environment detected but no token available - skipping private module setup', 'info');
log('This is expected for forked PRs or environments without access to private modules', 'info');
return false; // Return false to indicate setup was skipped
} else if (usingHTTPSGitAuth()) {
submoduleUrl = `https://github.com/${GITHUB_ORG}/${REPO_NAME}.git`;
} else {
// Local development with SSH
submoduleUrl = `git@github.com:${GITHUB_ORG}/${REPO_NAME}.git`;
}
try {
// Check if submodule is registered in .gitmodules (at repo root)
const gitmodulesPath = path.join(REPO_ROOT, '.gitmodules');
const gitmodulesExists = fs.existsSync(gitmodulesPath);
const gitmodulesContent = gitmodulesExists ? fs.readFileSync(gitmodulesPath, 'utf8') : '';
const isSubmoduleRegistered =
gitmodulesExists && gitmodulesContent.includes('[submodule "packages/mobile-sdk-alpha/mobile-sdk-native"]');
if (process.env.DEBUG_SETUP === 'true') {
log(`Environment: CI=${isCI}, hasAppToken=${!!appToken}, hasRepoToken=${!!repoToken}`, 'info');
log(`Submodule registered: ${isSubmoduleRegistered}`, 'info');
}
// Check if submodule directory exists and has content
const submoduleExists = fs.existsSync(PRIVATE_MODULE_PATH);
let submoduleHasContent = false;
try {
submoduleHasContent = submoduleExists && fs.readdirSync(PRIVATE_MODULE_PATH).length > 0;
} catch {
// Directory might not be readable, treat as empty
submoduleHasContent = false;
}
log(`Submodule directory exists: ${submoduleExists}, has content: ${submoduleHasContent}`, 'info');
// If submodule is registered, update its URL first (important for CI where we switch from SSH to HTTPS)
if (isSubmoduleRegistered) {
log(`Submodule is registered, updating URL from SSH to HTTPS...`, 'info');
log(`Target URL: ${submoduleUrl}`, 'info');
// Update submodule URL using git submodule set-url (Git 2.25+)
try {
const setUrlResult = runCommand(
`git submodule set-url packages/mobile-sdk-alpha/mobile-sdk-native "${submoduleUrl}"`,
{ stdio: 'pipe' },
REPO_ROOT,
);
log('Updated submodule URL using git submodule set-url', 'success');
log(`Command result: ${setUrlResult}`, 'info');
} catch (error) {
log(`git submodule set-url failed: ${error.message}`, 'warning');
// Fallback: Update .gitmodules file directly
try {
let gitmodulesContent = fs.readFileSync(gitmodulesPath, 'utf8');
log(`Current .gitmodules content:\n${gitmodulesContent}`, 'info');
// Replace the URL for mobile-sdk-native submodule
const oldContent = gitmodulesContent;
gitmodulesContent = gitmodulesContent.replace(
/(\[submodule\s+"packages\/mobile-sdk-alpha\/mobile-sdk-native"\]\s+path\s*=\s*packages\/mobile-sdk-alpha\/mobile-sdk-native\s+url\s*=\s*)[^\s]+/,
`$1${submoduleUrl}`,
);
if (oldContent !== gitmodulesContent) {
fs.writeFileSync(gitmodulesPath, gitmodulesContent, 'utf8');
log('Updated .gitmodules with new submodule URL', 'success');
log(`New .gitmodules content:\n${gitmodulesContent}`, 'info');
} else {
log('No changes made to .gitmodules - regex may not match', 'warning');
}
} catch (fallbackError) {
log(`Could not update .gitmodules: ${fallbackError.message}`, 'error');
}
}
}
// If directory exists but is empty, remove it so we can re-initialize
if (submoduleExists && !submoduleHasContent) {
log('Submodule directory exists but is empty, removing...', 'info');
runCommand(`rm -rf "${path.relative(REPO_ROOT, PRIVATE_MODULE_PATH)}"`, { stdio: 'pipe' }, REPO_ROOT);
}
if (isSubmoduleRegistered) {
// Submodule is registered, update/init it
log('Updating and initializing submodule...', 'info');
try {
const updateResult = runCommand(
`git submodule update --init --recursive packages/mobile-sdk-alpha/mobile-sdk-native`,
{},
REPO_ROOT,
);
log(`Submodule update completed: ${updateResult}`, 'success');
} catch (error) {
log(`Submodule update failed: ${error.message}`, 'error');
throw error;
}
} else {
// Submodule not registered, add it
log('Adding submodule...', 'info');
const addCommand = `git submodule add -b ${BRANCH} "${submoduleUrl}" packages/mobile-sdk-alpha/mobile-sdk-native`;
if (isCI && (appToken || repoToken)) {
// Security: Run command silently to avoid token exposure in logs
runCommand(addCommand, { stdio: 'pipe' }, REPO_ROOT);
} else {
runCommand(addCommand, {}, REPO_ROOT);
}
}
log(`Successfully set up ${REPO_NAME} as submodule`, 'success');
return true; // Return true to indicate successful setup
} catch (error) {
if (isCI) {
log('Submodule setup failed in CI environment. Check repository access/credentials configuration.', 'error');
} else {
log('Submodule setup failed. Ensure you have SSH access to the repository.', 'error');
}
throw error;
}
}
function validateSetup() {
const expectedFiles = ['src/main/java', 'src/main/res'];
for (const file of expectedFiles) {
const fullPath = path.join(PRIVATE_MODULE_PATH, file);
if (!fs.existsSync(fullPath)) {
throw new Error(`Required file not found: ${file}`);
}
}
log('Private module validation passed', 'success');
}
function scrubGitRemoteUrl() {
try {
const cleanUrl = `https://github.com/${GITHUB_ORG}/${REPO_NAME}.git`;
runCommand(`git -C mobile-sdk-native remote set-url origin "${cleanUrl}"`, { stdio: 'pipe' });
log('Git remote URL scrubbed of credentials', 'success');
} catch {
log('Failed to scrub git remote URL (non-critical)', 'warning');
}
}
function setupMobileSDKNative() {
log(`Starting setup of ${REPO_NAME} as submodule...`, 'info');
// Setup the submodule
const setupSuccessful = setupSubmodule();
// If setup was skipped (e.g., in forked PRs), exit gracefully
if (setupSuccessful === false) {
log(`${REPO_NAME} setup skipped - private module not available`, 'warning');
return;
}
// Security: Remove credential-embedded remote URL after setup
if (isCI && (appToken || repoToken) && !isDryRun) {
scrubGitRemoteUrl();
}
// Validate the setup
if (!isDryRun) {
validateSetup();
}
log(`${REPO_NAME} submodule setup complete!`, 'success');
log('💡 You can now work directly in mobile-sdk-native/ with full git history', 'info');
}
// Main execution
if (require.main === module) {
setupMobileSDKNative();
}
module.exports = { setupMobileSDKNative };