diff --git a/packages/@apphosting/adapter-angular/src/bin/build.spec.ts b/packages/@apphosting/adapter-angular/src/bin/build.spec.ts index 138cc877..2e500c8b 100644 --- a/packages/@apphosting/adapter-angular/src/bin/build.spec.ts +++ b/packages/@apphosting/adapter-angular/src/bin/build.spec.ts @@ -9,6 +9,7 @@ import { OutputBundleOptions } from "../interface.js"; describe("build commands", () => { let tmpDir: string; let outputBundleOptions: OutputBundleOptions; + let defaultAngularVersion: string; beforeEach(() => { tmpDir = generateTmpDir(); outputBundleOptions = { @@ -20,16 +21,18 @@ describe("build commands", () => { serverFilePath: resolve(tmpDir, ".apphosting", "dist", "server", "server.mjs"), needsServerGenerated: false, }; + defaultAngularVersion = "17.3.8"; }); it("expects all output bundle files to be generated", async () => { - const { generateOutputDirectory, validateOutputDirectory } = await importUtils; + const { generateOutputDirectory, validateOutputDirectory, createMetadata } = await importUtils; const files = { "dist/test/browser/browserfile": "", "dist/test/server/server.mjs": "", }; + const packageVersion = createMetadata(defaultAngularVersion).adapterVersion; generateTestFiles(tmpDir, files); - await generateOutputDirectory(tmpDir, outputBundleOptions); + await generateOutputDirectory(tmpDir, outputBundleOptions, defaultAngularVersion); await validateOutputDirectory(outputBundleOptions); const expectedFiles = { @@ -42,6 +45,11 @@ neededDirs: staticAssets: - .apphosting/dist/browser env: [] +metadata: + adapterPackageName: "@apphosting/adapter-angular" + adapterVersion: ${packageVersion} + framework: angular + frameworkVersion: 17.3.8 `, }; validateTestFiles(tmpDir, expectedFiles); @@ -56,22 +64,12 @@ env: [] generateTestFiles(tmpDir, files); await generateOutputDirectory(tmpDir, outputBundleOptions, "17.3.2"); - const expectedFiles = { - ".apphosting/dist/browser/browserfile": "", - ".apphosting/dist/server/server.mjs": "", - ".apphosting/bundle.yaml": ` -runCommand: node .apphosting/dist/server/server.mjs -neededDirs: - - .apphosting -staticAssets: - - .apphosting/dist/browser -env: + const expectedContents = `env: - variable: SSR_PORT value: "8080" availability: RUNTIME -`, - }; - validateTestFiles(tmpDir, expectedFiles); +`; + validateFileExistsAndContains(tmpDir, ".apphosting/bundle.yaml", expectedContents); }); it("test failed validateOutputDirectory", async () => { @@ -81,7 +79,7 @@ env: "dist/test/server/notserver.mjs": "", }; generateTestFiles(tmpDir, files); - await generateOutputDirectory(tmpDir, outputBundleOptions); + await generateOutputDirectory(tmpDir, outputBundleOptions, defaultAngularVersion); assert.rejects(async () => await validateOutputDirectory(outputBundleOptions)); }); @@ -134,3 +132,17 @@ function validateTestFiles(baseDir: string, expectedFiles: Object): void { assert.deepEqual(ignoreBlankLines(contents), ignoreBlankLines(expectedContents)); }); } + +function validateFileExistsAndContains( + baseDir: string, + expectedFileName: string, + expectedContents: string, +): void { + const fileToRead = path.join(baseDir, expectedFileName); + assert.ok(fs.existsSync(fileToRead), `File '${fileToRead}' does not exist.`); + const contents = fs.readFileSync(fileToRead).toString(); + assert.ok( + contents.includes(expectedContents), + `Actual contents do not contain expected contents.\nExpected contained contents:\n${expectedContents}\nActual:\n${contents}`, + ); +} diff --git a/packages/@apphosting/adapter-angular/src/bin/build.ts b/packages/@apphosting/adapter-angular/src/bin/build.ts index f60b2a7a..72ad102a 100644 --- a/packages/@apphosting/adapter-angular/src/bin/build.ts +++ b/packages/@apphosting/adapter-angular/src/bin/build.ts @@ -12,6 +12,9 @@ const opts = getBuildOptions(); // Check build conditions, which vary depending on your project structure (standalone or monorepo) await checkBuildConditions(opts); +if (!process.env.FRAMEWORK_VERSION) { + throw new Error("Could not find the angular version of the application"); +} // enable JSON build logs for application builder process.env.NG_BUILD_LOGS_JSON = "1"; diff --git a/packages/@apphosting/adapter-angular/src/interface.ts b/packages/@apphosting/adapter-angular/src/interface.ts index b7e27169..5826d2a4 100644 --- a/packages/@apphosting/adapter-angular/src/interface.ts +++ b/packages/@apphosting/adapter-angular/src/interface.ts @@ -25,6 +25,14 @@ export enum Availability { Runtime = "RUNTIME", } +// Metadata schema for bundle.yaml outputted by angular adapter +export interface Metadata { + adapterPackageName: string; + adapterVersion: string; + framework: string; + frameworkVersion: string; +} + // valid manifest schema export interface ValidManifest { errors: string[]; diff --git a/packages/@apphosting/adapter-angular/src/utils.ts b/packages/@apphosting/adapter-angular/src/utils.ts index c8ab20fc..de96f270 100644 --- a/packages/@apphosting/adapter-angular/src/utils.ts +++ b/packages/@apphosting/adapter-angular/src/utils.ts @@ -8,6 +8,7 @@ import { stringify as yamlStringify } from "yaml"; import { Availability, EnvironmentVariable, + Metadata, OutputBundleOptions, OutputPaths, buildManifestSchema, @@ -17,7 +18,7 @@ import stripAnsi from "strip-ansi"; import { BuildOptions } from "@apphosting/common"; // fs-extra is CJS, readJson can't be imported using shorthand -export const { writeFile, move, readJson, mkdir, copyFile } = fsExtra; +export const { writeFile, move, readJson, mkdir, copyFile, readFileSync, existsSync } = fsExtra; const require = createRequire(import.meta.url); const __filename = fileURLToPath(import.meta.url); @@ -142,6 +143,23 @@ function extractManifestOutput(output: string): string { return stripAnsi(output.substring(start, end + 1)); } +/** + * Create metadata needed for outputting adapter and framework metrics in bundle.yaml. + */ +export function createMetadata(angularVersion: string): Metadata { + const packageJsonPath = `${__dirname}/../package.json`; + if (!existsSync(packageJsonPath)) { + throw new Error(`Angular adapter package.json file does not exist at ${packageJsonPath}`); + } + const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); + return { + adapterPackageName: packageJson.name, + adapterVersion: packageJson.version, + framework: "angular", + frameworkVersion: angularVersion, + }; +} + /** * Move the base output directory, which contains the server and browser bundle directory, and prerendered routes * as well as generating bundle.yaml. @@ -149,7 +167,7 @@ function extractManifestOutput(output: string): string { export async function generateOutputDirectory( cwd: string, outputBundleOptions: OutputBundleOptions, - angularVersion?: string, + angularVersion: string, ): Promise { await move(outputBundleOptions.baseDirectory, outputBundleOptions.outputBaseDirectory, { overwrite: true, @@ -161,10 +179,10 @@ export async function generateOutputDirectory( } // add environment variable to bundle.yaml if needed for specific versions -function addBundleYamlEnvVar(angularVersion?: string): EnvironmentVariable[] { +function generateEnvVars(angularVersion: string): EnvironmentVariable[] { const runtimeEnvVars: EnvironmentVariable[] = []; // add env var to solve angular port issue, existing only for Angular v17.3.2 (b/332896115) - if (angularVersion && angularVersion === "17.3.2") { + if (angularVersion === "17.3.2") { const ssrPortEnvVar: EnvironmentVariable = { variable: "SSR_PORT", value: "8080", @@ -179,16 +197,16 @@ function addBundleYamlEnvVar(angularVersion?: string): EnvironmentVariable[] { async function generateBundleYaml( outputBundleOptions: OutputBundleOptions, cwd: string, - angularVersion?: string, + angularVersion: string, ): Promise { - const runtimeEnvVars = addBundleYamlEnvVar(angularVersion); await writeFile( outputBundleOptions.bundleYamlPath, yamlStringify({ runCommand: `node ${normalize(relative(cwd, outputBundleOptions.serverFilePath))}`, neededDirs: [normalize(relative(cwd, outputBundleOptions.outputDirectory))], staticAssets: [normalize(relative(cwd, outputBundleOptions.browserDirectory))], - env: runtimeEnvVars, + env: generateEnvVars(angularVersion), + metadata: createMetadata(angularVersion), }), ); }