Skip to content

Commit

Permalink
Use chrome containers instead of chrome build repo.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyperkid123 committed Jun 3, 2024
1 parent 69399c1 commit 48da49f
Show file tree
Hide file tree
Showing 8 changed files with 532 additions and 168 deletions.
489 changes: 325 additions & 164 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions packages/config-utils/src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ export type ProxyOptions = {
useAgent?: boolean;
useDevBuild?: boolean;
localApps?: string;
/**
* Used to block running chrome from build repos.
* Chrome should be running from container from now on.
*/
blockLegacyChrome?: boolean;
};

const proxy = ({
Expand All @@ -140,6 +145,7 @@ const proxy = ({
useAgent = true,
useDevBuild = true,
localApps = process.env.LOCAL_APPS,
blockLegacyChrome = false,
}: ProxyOptions) => {
const proxy: ProxyConfigItem[] = [];
const majorEnv = env.split('-')[0];
Expand Down Expand Up @@ -369,14 +375,14 @@ const proxy = ({
* Allow serving chrome assets
* This will allow running chrome as a host application
*/
if (!isChrome) {
if (localChrome || (!blockLegacyChrome && !isChrome)) {
let chromePath = localChrome;
if (standaloneConfig) {
if (standaloneConfig.chrome) {
chromePath = resolvePath(reposDir, standaloneConfig.chrome.path);
keycloakUri = standaloneConfig.chrome.keycloakUri;
}
} else if (!localChrome && useProxy) {
} else if (!blockLegacyChrome && !localChrome && useProxy) {
const chromeConfig = typeof defaultServices.chrome === 'function' ? defaultServices.chrome({}) : defaultServices.chrome;

const chromeEnv = useDevBuild ? (env.includes('-beta') ? 'dev-beta' : 'dev-stable') : env;
Expand Down
7 changes: 5 additions & 2 deletions packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
},
"homepage": "https://github.com/RedHatInsights/frontend-components/tree/master/packages/config#readme",
"scripts": {
"build": "tsc"
"build": "tsc",
"watch": "tsc -w"
},
"dependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.8",
Expand Down Expand Up @@ -64,12 +65,14 @@
"ts-loader": "^9.4.4",
"url": "^0.11.0",
"util": "^0.12.4",
"wait-on": "^7.2.0",
"webpack": "^5.88.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "4.15.1",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/inquirer": "^9.0.3"
"@types/inquirer": "^9.0.3",
"@types/wait-on": "^5.3.4"
}
}
20 changes: 20 additions & 0 deletions packages/config/src/bin/dev-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import inquirer from 'inquirer';
const { resolve } = require('path');
const { spawn } = require('child_process');
import { getWebpackConfigPath, validateFECConfig } from './common';
import serveChrome from './serve-chrome';
import { LogType, fecLogger } from '@redhat-cloud-services/frontend-components-config-utilities';

async function setEnv(cwd: string) {
return inquirer
Expand Down Expand Up @@ -38,6 +40,7 @@ async function devScript(
cwd: string
) {
try {
let localChrome = false;
let fecConfig: any = {};
let configPath;
if (typeof argv.webpackConfig !== 'undefined') {
Expand All @@ -46,6 +49,7 @@ async function devScript(
// validate the FEC config only if a custom webpack config is not provided
validateFECConfig(cwd);
fecConfig = require(process.env.FEC_CONFIG_PATH!);
localChrome = fecConfig.localChrome;
configPath = resolve(__dirname, './dev.webpack.config.js');
}

Expand Down Expand Up @@ -74,6 +78,22 @@ async function devScript(
process.env.PORT = argv.port;
}

// ignore chrome server if a localChrome is provided
if (!localChrome) {
// get the directory if the build
// hsa to require here after all FEC env variables are set
const devConfig = require('./dev.webpack.config');
const outputPath = devConfig.output?.path;
// start chrome frontend server
try {
await serveChrome(outputPath, process.env.CLOUDOT_ENV === 'prod', argv.uiEnv === 'beta');
} catch (error) {
fecLogger(LogType.error, 'Unable to start local Chrome UI server!');
fecLogger(LogType.error, error);
process.exit(1);
}
}

spawn(`npm exec -- webpack serve -c ${configPath}`, [], {
stdio: [process.stdout, process.stdout, process.stdout],
cwd,
Expand Down
5 changes: 5 additions & 0 deletions packages/config/src/bin/dev.webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ProxyConfigArrayItem } from 'webpack-dev-server';
import config, { FrontendEnv } from '../lib';
import FECConfiguration from '../lib/fec.config';
import commonPlugins from './webpack.plugins';
import { EXPOSED_PORT } from './serve-chrome';
const fecConfig: FECConfiguration = require(process.env.FEC_CONFIG_PATH!);

type Configuration = import('webpack').Configuration;
Expand Down Expand Up @@ -40,6 +41,9 @@ const { plugins: externalPlugins = [], interceptChromeConfig, routes, ...externa

const internalProxyRoutes: { [endpoint: string]: ProxyConfigArrayItem } = {
...routes,
'/apps/chrome': {
target: `http://127.0.0.1:${EXPOSED_PORT}`,
},
...(interceptChromeConfig === true
? {
'/api/chrome-service/v1/static': {
Expand All @@ -60,6 +64,7 @@ const { config: webpackConfig, plugins } = config({
deployment: isBeta ? 'beta/apps' : 'apps',
env: `${process.env.CLOUDOT_ENV}-${isBeta === true ? 'beta' : 'stable'}` as FrontendEnv,
rootFolder: process.env.FEC_ROOT_DIR || process.cwd(),
blockLegacyChrome: true,
});
plugins.push(...commonPlugins, ...externalPlugins);

Expand Down
165 changes: 165 additions & 0 deletions packages/config/src/bin/serve-chrome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import axios from 'axios';
import path from 'path';
import fs from 'fs';
import { execSync, spawn } from 'child_process';
import { load } from 'js-yaml';
import { LogType, fecLogger } from '@redhat-cloud-services/frontend-components-config-utilities';
import waitOn from 'wait-on';

const REPO_OWNER = 'RedHatInsights';
const REPO_NAME = 'insights-chrome';
const CONTAINER_PORT = 8000;
export const EXPOSED_PORT = 9999;

type ContainerRuntime = 'docker' | 'podman';
let execBin: ContainerRuntime | undefined = undefined;

const chromeDeploymentConfig = {
repo: 'git@gitlab.cee.redhat.com:service/app-interface.git',
deployFile: 'data/services/insights/frontend-base/deploy.yml',
stablePath: 'resourceTemplates[0].targets[4].ref',
tarTarget: path.resolve(__dirname, 'chrome-deploy/'),
};

const checkoutCommand = `git archive --remote=${chromeDeploymentConfig.repo} HEAD ${chromeDeploymentConfig.deployFile} | tar xvf - -C ${chromeDeploymentConfig.tarTarget}`;

function checkContainerRuntime(): ContainerRuntime {
try {
if (execSync('which docker').toString().trim().length > 0) {
return 'docker';
}

if (execSync('which podman').toString().trim().length > 0) {
return 'podman';
}
} catch (error) {
throw new Error('No container runtime found');
}

throw new Error('No container runtime found');
}

async function getLatestCommits(): Promise<string> {
const { data } = await axios.get(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/commits`, {
headers: {
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
},
params: {
per_page: 1,
},
});
if (data.length === 0) {
throw new Error('No commits for chrome found!');
}

const { sha } = data[0];
return sha.substring(0, 7);
}

type Deployment = {
resourceTemplates: {
name: string;
targets: {
ref: string;
namespace: {
$ref: string;
};
}[];
}[];
};

async function getProdRelease() {
try {
if (!fs.existsSync(chromeDeploymentConfig.tarTarget)) {
fs.mkdirSync(chromeDeploymentConfig.tarTarget);
}
execSync(checkoutCommand, {
stdio: 'inherit',
});

const deployment = load(
fs.readFileSync(path.join(chromeDeploymentConfig.tarTarget, chromeDeploymentConfig.deployFile), {
encoding: 'utf-8',
})
) as Deployment;
const chromeProd = deployment.resourceTemplates
.find((template) => {
return template.name === 'insights-chrome';
})
?.targets.find((target) => {
return target.namespace.$ref.includes('prod-frontends');
});
if (!chromeProd) {
throw new Error('Unable to find chrome prod deployment configuration.');
}

return chromeProd.ref.substring(0, 7);
} catch (error) {
fecLogger(LogType.error, error);
fecLogger(LogType.warn, 'Unable to find chrome prod deployment! Falling back to latest image.');
return getLatestCommits();
}

// const { data: deploymentYaml } = await axios.get(deploymentSource);
// console.log(deploymentYaml);
}

function pullImage(tag: string) {
execSync(`${execBin} pull quay.io/cloudservices/insights-chrome-frontend:${tag}`, {
stdio: 'inherit',
});
}

function startServer(tag: string) {
try {
execSync(`${execBin} stop fec-chrome-local`, {
stdio: 'inherit',
});
execSync(`${execBin} rm fec-chrome-local`, {
stdio: 'inherit',
});
} catch (error) {
fecLogger(LogType.info, 'No existing chrome container found');
}
const child = spawn(
`${execBin} run -p ${EXPOSED_PORT}:${CONTAINER_PORT} --name fec-chrome-local quay.io/cloudservices/insights-chrome-frontend:${tag}`,
[],
{
stdio: 'ignore',
shell: true,
}
);
return child;
}

function copyIndex(path: string, isPreview = false) {
const copyCommand = `docker cp fec-chrome-local:/opt/app-root/src/build/${isPreview ? 'preview' : 'stable'}/index.html ${path}`;
execSync(copyCommand, {
stdio: 'inherit',
});
}

async function serveChrome(distPath: string, isProd = false, isPreview = false) {
console.log('distPath', distPath);
if (!distPath) {
fecLogger(LogType.error, 'No distPath provided');
process.exit(1);
}
fecLogger(LogType.info, 'Starting chrome server...');
execBin = checkContainerRuntime();
let tag: string;
if (isProd) {
tag = await getProdRelease();
} else {
tag = await getLatestCommits();
}
pullImage(tag);
startServer(tag);
await waitOn({
resources: [`http://127.0.0.1:${EXPOSED_PORT}`],
});
copyIndex(distPath, isPreview);
}

export default serveChrome;
3 changes: 3 additions & 0 deletions packages/config/src/lib/createConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface CreateConfigOptions extends CommonConfigOptions {
cacheConfig?: Partial<CacheOptions>;
nodeModulesDirectories?: string[];
resolve?: ResolveOptions;
blockLegacyChrome?: boolean;
}

export const createConfig = ({
Expand Down Expand Up @@ -91,6 +92,7 @@ export const createConfig = ({
resolve = {},
// additional node_modules dirs for searchIgnoredStyles, usefull in monorepo scenario
nodeModulesDirectories = [],
blockLegacyChrome,
}: CreateConfigOptions): Configuration => {
if (typeof _unstableHotReload !== 'undefined') {
fecLogger(LogType.warn, `The _unstableHotReload option in shared webpack config is deprecated. Use hotReload config instead.`);
Expand Down Expand Up @@ -295,6 +297,7 @@ export const createConfig = ({
bounceProd,
useAgent,
useDevBuild,
blockLegacyChrome,
}),
},
};
Expand Down
1 change: 1 addition & 0 deletions packages/config/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type FecConfigurationOptions = Omit<CreateConfigOptions, 'publicPath' | 'appEntr
deployment?: string;
debug?: boolean;
appEntry?: string;
blockLegacyChrome?: boolean;
};

const createFecConfig = (
Expand Down

0 comments on commit 48da49f

Please sign in to comment.