Skip to content

Commit

Permalink
[tools] Publishing canary templates in the publish script (#27655)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsapeta authored and marklawlor committed Mar 18, 2024
1 parent 849fb0d commit ab6ce1b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 5 deletions.
57 changes: 53 additions & 4 deletions tools/src/ProjectTemplates.ts
Expand Up @@ -10,20 +10,69 @@ export type Template = {
path: string;
};

const DEPENDENCIES_KEYS = ['dependencies', 'devDependencies', 'peerDependencies'];

export async function getAvailableProjectTemplatesAsync(): Promise<Template[]> {
const templates = await fs.readdir(TEMPLATES_DIR);
const templates = (await fs.readdir(TEMPLATES_DIR, { withFileTypes: true }))
.filter((dirent) => dirent.isDirectory())
.map((dirent) => path.join(TEMPLATES_DIR, dirent.name));

return Promise.all<Template>(
templates.map(async (template) => {
templates.map(async (templatePath) => {
const packageJson = await JsonFile.readAsync<Template>(
path.join(TEMPLATES_DIR, template, 'package.json')
path.join(templatePath, 'package.json')
);

return {
name: packageJson.name,
version: packageJson.version,
path: path.join(TEMPLATES_DIR, template),
path: templatePath,
};
})
);
}

/**
* Updates version of the template and its dependencies.
*/
export async function updateTemplateVersionsAsync(
templatePath: string,
templateVersion: string,
dependenciesToUpdate: Record<string, string>
): Promise<void> {
const packageJsonPath = path.join(templatePath, 'package.json');
const packageJson = require(packageJsonPath);

packageJson.version = templateVersion;

for (const dependencyKey of DEPENDENCIES_KEYS) {
const dependencies = packageJson[dependencyKey];

if (!dependencies) {
continue;
}
for (const dependencyName in dependencies) {
const currentVersion = dependencies[dependencyName];
const targetVersion = resolveTargetVersionRange(
dependenciesToUpdate[dependencyName],
currentVersion
);

if (targetVersion && targetVersion !== currentVersion) {
packageJson[dependencyKey][dependencyName] = targetVersion;
}
}
}
await JsonFile.writeAsync(packageJsonPath, packageJson);
}

/**
* Finds target version range, that is usually `bundledModuleVersion` param,
* but in some specific cases we want to use different version range.
*/
function resolveTargetVersionRange(targetVersionRange: string, currentVersion: string) {
if (currentVersion === '*') {
return currentVersion;
}
return targetVersionRange;
}
58 changes: 57 additions & 1 deletion tools/src/publish-packages/tasks/publishCanary.ts
@@ -1,4 +1,5 @@
import chalk from 'chalk';
import path from 'path';
import semver from 'semver';

import { checkEnvironmentTask } from './checkEnvironmentTask';
Expand All @@ -10,20 +11,31 @@ import { updateBundledNativeModulesFile } from './updateBundledNativeModulesFile
import { updateModuleTemplate } from './updateModuleTemplate';
import { updatePackageVersions } from './updatePackageVersions';
import { updateWorkspaceProjects } from './updateWorkspaceProjects';
import { PACKAGES_DIR } from '../../Constants';
import Git from '../../Git';
import logger from '../../Logger';
import { getPackageViewAsync, publishPackageAsync } from '../../Npm';
import {
getAvailableProjectTemplatesAsync,
updateTemplateVersionsAsync,
} from '../../ProjectTemplates';
import { sdkVersionAsync } from '../../ProjectVersions';
import { Task } from '../../TasksRunner';
import { runWithSpinner } from '../../Utils';
import { CommandOptions, Parcel, TaskArgs } from '../types';

const { cyan } = chalk;
const { cyan, green } = chalk;

/**
* An array of packages whose version is constrained to the SDK version.
*/
const SDK_CONSTRAINED_PACKAGES = ['expo', 'jest-expo', '@expo/config-types'];

/**
* Path to `bundledNativeModules.json` file.
*/
const BUNDLED_NATIVE_MODULES_PATH = path.join(PACKAGES_DIR, 'expo', 'bundledNativeModules.json');

/**
* Prepare packages to be published as canaries.
*/
Expand Down Expand Up @@ -52,6 +64,49 @@ export const prepareCanaries = new Task<TaskArgs>(
}
);

const publishCanaryProjectTemplates = new Task<TaskArgs>(
{
name: 'publishCanaryProjectTemplates',
dependsOn: [prepareCanaries],
},
async (parcels: Parcel[], options: CommandOptions) => {
await runWithSpinner(
'Updating and publishing project templates',
async (step) => {
const templates = await getAvailableProjectTemplatesAsync();
const bundledNativeModules = require(BUNDLED_NATIVE_MODULES_PATH);
const expoVersion = await sdkVersionAsync();
const dependenciesToUpdate = {
...bundledNativeModules,
expo: `~${expoVersion}`,
};

for (const template of templates) {
step.start(`Updating and publishing ${green(template.name)}`);

const templateView = await getPackageViewAsync(template.name);

// Canary project template uses the same version as the `expo` package that it depends on.
const canaryVersion = findNextAvailableCanaryVersion(
expoVersion,
templateView?.versions ?? []
);

// Update versions of the dependencies based on `bundledNativeModules.json`.
await updateTemplateVersionsAsync(template.path, canaryVersion, dependenciesToUpdate);

// Publish the project template with a `canary` tag.
await publishPackageAsync(template.path, {
tagName: 'canary',
dryRun: options.dry,
});
}
},
'Updated and published project templates'
);
}
);

/**
* Cleans up all the changes that were made by previously run tasks.
*/
Expand Down Expand Up @@ -105,6 +160,7 @@ export const publishCanaryPipeline = new Task<TaskArgs>(
updateWorkspaceProjects,
packPackageToTarball,
publishPackages,
publishCanaryProjectTemplates,
cleanWorkingTree,
],
},
Expand Down

0 comments on commit ab6ce1b

Please sign in to comment.