From b435d50818e9b995a770f109a7e79a1d1cd430ab Mon Sep 17 00:00:00 2001 From: phani-srikar Date: Thu, 13 Jul 2023 10:29:19 -0700 Subject: [PATCH] feat: support e2e workflow on codebuild --- .codebuild/e2e_workflow.yml | 27 +++++--- .codebuild/e2e_workflow_base.yml | 11 ++- .codebuild/run_e2e_tests.yml | 5 +- .codebuild/run_ios_modelgen_e2e_test.yml | 27 ++++++++ .../scripts/run-ios-modelgen-e2e-test.sh | 68 +++++++++++++++++++ .../amplify-codegen-e2e-core/package.json | 6 +- .../src/categories/codegen.ts | 14 ++++ .../amplify-codegen-e2e-core/src/index.ts | 11 ++- .../src/__tests__/build-app-swift.test.ts | 16 ++--- .../model-introspection-codegen.test.ts | 6 +- .../src/configure_tests.ts | 9 ++- scripts/split-e2e-tests-codebuild.ts | 7 +- 12 files changed, 171 insertions(+), 36 deletions(-) create mode 100644 .codebuild/run_ios_modelgen_e2e_test.yml create mode 100644 .codebuild/scripts/run-ios-modelgen-e2e-test.sh diff --git a/.codebuild/e2e_workflow.yml b/.codebuild/e2e_workflow.yml index 3d06ed2e..161f7e9a 100644 --- a/.codebuild/e2e_workflow.yml +++ b/.codebuild/e2e_workflow.yml @@ -2,7 +2,7 @@ version: 0.2 env: shell: bash - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE batch: fast-fail: false build-graph: @@ -28,11 +28,20 @@ batch: compute-type: BUILD_GENERAL1_MEDIUM depend-on: - build_linux + - identifier: build_app_swift + buildspec: .codebuild/run_ios_modelgen_e2e_test.yml + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + TEST_SUITE: src/__tests__/build-app-swift.test.ts + CLI_REGION: us-east-2 + depend-on: + - publish_to_local_registry - identifier: >- add_codegen_ios_configure_codegen_android_configure_codegen_js_graphql_codegen_android buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/add-codegen-ios.test.ts|src/__tests__/configure-codegen-android.test.ts|src/__tests__/configure-codegen-js.test.ts|src/__tests__/graphql-codegen-android.test.ts @@ -43,7 +52,7 @@ batch: graphql_codegen_js_remove_codegen_android_remove_codegen_ios_add_codegen_android buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/graphql-codegen-js.test.ts|src/__tests__/remove-codegen-android.test.ts|src/__tests__/remove-codegen-ios.test.ts|src/__tests__/add-codegen-android.test.ts @@ -54,7 +63,7 @@ batch: configure_codegen_ios_datastore_modelgen_android_datastore_modelgen_js_feature_flags buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/configure-codegen-ios.test.ts|src/__tests__/datastore-modelgen-android.test.ts|src/__tests__/datastore-modelgen-js.test.ts|src/__tests__/feature-flags.test.ts @@ -65,7 +74,7 @@ batch: graphql_codegen_ios_add_codegen_js_datastore_modelgen_ios_remove_codegen_js buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/graphql-codegen-ios.test.ts|src/__tests__/add-codegen-js.test.ts|src/__tests__/datastore-modelgen-ios.test.ts|src/__tests__/remove-codegen-js.test.ts @@ -76,7 +85,7 @@ batch: datastore_modelgen_flutter_env_codegen_model_introspection_codegen_pull_codegen buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/datastore-modelgen-flutter.test.ts|src/__tests__/env-codegen.test.ts|src/__tests__/model-introspection-codegen.test.ts|src/__tests__/pull-codegen.test.ts @@ -87,7 +96,7 @@ batch: push_codegen_ios_push_codegen_android_graphql_documents_generator_push_codegen_js buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: >- src/__tests__/push-codegen-ios.test.ts|src/__tests__/push-codegen-android.test.ts|src/__tests__/graphql-documents-generator.test.ts|src/__tests__/push-codegen-js.test.ts @@ -97,7 +106,7 @@ batch: - identifier: build_app_ts buildspec: .codebuild/run_e2e_tests.yml env: - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE variables: TEST_SUITE: src/__tests__/build-app-ts.test.ts CLI_REGION: ap-southeast-1 @@ -106,7 +115,7 @@ batch: - identifier: cleanup_e2e_resources buildspec: .codebuild/cleanup_e2e_resources.yml env: - compute-type: BUILD_GENERAL1_SMALL + compute-type: BUILD_GENERAL1_MEDIUM depend-on: - >- add_codegen_ios_configure_codegen_android_configure_codegen_js_graphql_codegen_android diff --git a/.codebuild/e2e_workflow_base.yml b/.codebuild/e2e_workflow_base.yml index 74d4e57b..2a707ecf 100644 --- a/.codebuild/e2e_workflow_base.yml +++ b/.codebuild/e2e_workflow_base.yml @@ -1,7 +1,7 @@ version: 0.2 env: shell: bash - compute-type: BUILD_GENERAL1_MEDIUM + compute-type: BUILD_GENERAL1_LARGE batch: fast-fail: false @@ -28,3 +28,12 @@ batch: compute-type: BUILD_GENERAL1_MEDIUM depend-on: - build_linux + - identifier: build_app_swift + buildspec: .codebuild/run_ios_modelgen_e2e_test.yml + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + TEST_SUITE: src/__tests__/build-app-swift.test.ts + CLI_REGION: us-east-2 + depend-on: + - publish_to_local_registry diff --git a/.codebuild/run_e2e_tests.yml b/.codebuild/run_e2e_tests.yml index 4a17505c..d2a3a8f2 100644 --- a/.codebuild/run_e2e_tests.yml +++ b/.codebuild/run_e2e_tests.yml @@ -7,13 +7,10 @@ env: CI: true CODEBUILD: true NODE_OPTIONS: --max-old-space-size=8096 - # mock values to test artifact scanning - ENV_VAR_WITH_SECRETS: 'MOCK_ENV_VAR_FOR_SCANNING_SECRETS' - MOCK_ENV_VAR_FOR_SCANNING_SECRETS: 'abc123xyz' + phases: build: commands: - # you can provide a codebuild source version to use old cache and skip all other jobs :) - source ./shared-scripts.sh && _runE2ETestsLinux post_build: commands: diff --git a/.codebuild/run_ios_modelgen_e2e_test.yml b/.codebuild/run_ios_modelgen_e2e_test.yml new file mode 100644 index 00000000..d6c7552b --- /dev/null +++ b/.codebuild/run_ios_modelgen_e2e_test.yml @@ -0,0 +1,27 @@ +version: 0.2 +env: + shell: bash + variables: + AMPLIFY_DIR: /root/.npm-global/lib/node_modules/@aws-amplify/cli-internal/bin + AMPLIFY_PATH: /root/.npm-global/lib/node_modules/@aws-amplify/cli-internal/bin/amplify + CI: true + CODEBUILD: true + NODE_OPTIONS: --max-old-space-size=8096 +phases: + build: + commands: + - source ./shared-scripts.sh && _runE2ETestsLinux + - export PATH_TO_MODELS=$CODEBUILD_SRC_DIR/packages/amplify-codegen-e2e-tests/test-apps/swift/amplify/generated + - cd $PATH_TO_MODELS && zip -r models.zip models + - aws s3 cp $PATH_TO_MODELS/models.zip s3://$ARTIFACT_BUCKET_NAME/models.zip + - export MODELS_S3_URL=$(aws s3 presign s3://$ARTIFACT_BUCKET_NAME/models.zip --expires-in 3600) + - cd $CODEBUILD_SRC_DIR && ./.codebuild/scripts/run-ios-modelgen-e2e-test.sh + post_build: + commands: + - aws sts get-caller-identity + - source ./shared-scripts.sh && _scanArtifacts + +artifacts: + files: + - $CODEBUILD_SRC_DIR/packages/amplify-codegen-e2e-tests/amplify-e2e-reports/* + discard-paths: yes \ No newline at end of file diff --git a/.codebuild/scripts/run-ios-modelgen-e2e-test.sh b/.codebuild/scripts/run-ios-modelgen-e2e-test.sh new file mode 100644 index 00000000..fc0a4da0 --- /dev/null +++ b/.codebuild/scripts/run-ios-modelgen-e2e-test.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +REPO_OWNER="aws-amplify" +REPO_NAME="amplify-codegen" + +# Function to get the latest workflow run ID +get_latest_run_id() { + latest_run_id=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runs?event=workflow_dispatch&per_page=1" | \ + jq -r '.workflow_runs[0].id') + echo "$latest_run_id" +} + +# Function to get the status of a workflow run +get_run_status() { + run_id="$1" + run_status=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runs/$run_id" | \ + jq -r '.status') + echo "$run_status" +} + +# Function to trigger a workflow dispatch event to run the e2e test +trigger_workflow() { + curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/workflows/build-swift-modelgen.yml/dispatches" \ + -d "{\"ref\":\"main\", \"inputs\":{\"MODELS_S3_URL\":\"${MODELS_S3_URL}\"}}" +} + +main() { + trigger_workflow + sleep 10 # Wait to allow for the workflow to be triggered + + # Get the latest run ID and initial status + latest_run_id=$(get_latest_run_id) + echo "Latest run ID: $latest_run_id" + latest_status=$(get_run_status "$latest_run_id") + timeout=$((SECONDS + 600)) # 600 seconds = 10 minutes + + # Continuously check for status until completion + while [[ "$latest_status" != "completed" && "$SECONDS" -lt "$timeout" ]]; do + echo "Test run status: $latest_status" + sleep 10 # Wait before checking again + latest_status=$(get_run_status "$latest_run_id") + done + + # Check if the run completed within the specified duration + if [[ "$latest_status" != "completed" ]]; then + echo "The test run did not complete within the specified duration." + exit 1 + fi + + # Check if the run failed and throw an error if it did + if [[ "$latest_status" == "failure" ]]; then + echo "The test run failed." + exit 1 + else + echo "The test run succeeded." + fi +} + +main diff --git a/packages/amplify-codegen-e2e-core/package.json b/packages/amplify-codegen-e2e-core/package.json index 3007123f..41d07039 100644 --- a/packages/amplify-codegen-e2e-core/package.json +++ b/packages/amplify-codegen-e2e-core/package.json @@ -32,7 +32,11 @@ "rimraf": "^3.0.0", "strip-ansi": "^6.0.0", "throat": "^5.0.0", - "uuid": "7.0.1" + "uuid": "7.0.1", + "ini": "^3.0.1" + }, + "devDependencies": { + "@types/ini": "^1.3.31" }, "peerDependencies": { "@aws-amplify/amplify-cli-core": "^4.0.0", diff --git a/packages/amplify-codegen-e2e-core/src/categories/codegen.ts b/packages/amplify-codegen-e2e-core/src/categories/codegen.ts index 98c4c72c..c0708d11 100644 --- a/packages/amplify-codegen-e2e-core/src/categories/codegen.ts +++ b/packages/amplify-codegen-e2e-core/src/categories/codegen.ts @@ -147,6 +147,20 @@ export function generateModelIntrospection(cwd: string, settings: { outputDir?: }); } +export function generateModelIntrospectionWithError(cwd: string, errMessage: string, settings: { outputDir?: string} = {}): Promise { + return new Promise((resolve, reject) => { + spawn(getCLIPath(), ['codegen', 'model-introspection', '--output-dir', settings.outputDir ?? ''], { cwd, stripColors: true }) + .wait(errMessage) + .run((err: Error) => { + if (!err) { + resolve(); + } else { + reject(err); + } + }); + }); +} + // CLI workflow to add codegen to non-Amplify JS project export function addCodegenNonAmplifyJS(cwd: string): Promise { return new Promise((resolve, reject) => { diff --git a/packages/amplify-codegen-e2e-core/src/index.ts b/packages/amplify-codegen-e2e-core/src/index.ts index 8fc03fa8..6c441701 100644 --- a/packages/amplify-codegen-e2e-core/src/index.ts +++ b/packages/amplify-codegen-e2e-core/src/index.ts @@ -3,6 +3,8 @@ import * as path from 'path'; import * as fs from 'fs-extra'; import { spawnSync, execSync } from 'child_process'; import { v4 as uuid } from 'uuid'; +import * as ini from 'ini'; +import { pathManager } from '@aws-amplify/amplify-cli-core'; export * from './configure/'; export * from './init/'; @@ -29,7 +31,14 @@ export function getCLIPath(testingWithLatestCodebase = false) { } export function isCI(): boolean { - return process.env.CI && process.env.CIRCLECI ? true : false; + return process.env.CI && (process.env.CIRCLECI || process.env.CODEBUILD) ? true : false; +} + +export function injectSessionToken(profileName: string) { + const credentialsContents = ini.parse(fs.readFileSync(pathManager.getAWSCredentialsFilePath()).toString()); + credentialsContents[profileName] = credentialsContents[profileName] || {}; + credentialsContents[profileName].aws_session_token = process.env.AWS_SESSION_TOKEN; + fs.writeFileSync(pathManager.getAWSCredentialsFilePath(), ini.stringify(credentialsContents)); } export function npmInstall(cwd: string) { diff --git a/packages/amplify-codegen-e2e-tests/src/__tests__/build-app-swift.test.ts b/packages/amplify-codegen-e2e-tests/src/__tests__/build-app-swift.test.ts index 782c12b4..c2163b2e 100644 --- a/packages/amplify-codegen-e2e-tests/src/__tests__/build-app-swift.test.ts +++ b/packages/amplify-codegen-e2e-tests/src/__tests__/build-app-swift.test.ts @@ -1,13 +1,11 @@ import { initProjectWithQuickstart, DEFAULT_IOS_CONFIG, - addApiWithBlankSchemaAndConflictDetection, updateApiSchemaWithText, generateModels, - swiftBuild, } from '@aws-amplify/amplify-codegen-e2e-core'; const { schemas } = require('@aws-amplify/graphql-schema-test-library'); -import { existsSync, writeFileSync, readdirSync, rmSync, readFileSync } from 'fs'; +import { writeFileSync, readdirSync, readFileSync } from 'fs'; import path from 'path'; const skip = new Set([ @@ -30,20 +28,20 @@ describe('build app - Swift', () => { }); afterEach(async () => { - await rmSync(path.join(projectRoot, 'amplify', 'generated', 'models'), { recursive: true, force: true }); writeFileSync(path.join(projectRoot, 'swift.xcodeproj', 'project.pbxproj'), projectPBXProjCache); }); Object.entries(schemas).forEach(([schemaName, schema]) => { // @ts-ignore const testName = `builds with ${schemaName}: ${schema.description}`; + const schemaFolderName = schemaName.replace(/[^a-zA-Z0-9]/g, ''); + const outputDir = path.join(projectRoot, 'amplify', 'generated', 'models', schemaFolderName); const testFunction = async () => { // @ts-ignore const schemaText = `input AMPLIFY { globalAuthRule: AuthRule = { allow: public } }\n${schema.sdl}`; console.log(schemaText); // log so that circleci does not timeout updateApiSchemaWithText(projectRoot, 'amplifyDatasource', schemaText); - await generateModels(projectRoot); - await swiftBuild(projectRoot, { ...config, scheme: 'swift' }); + await generateModels(projectRoot, outputDir); }; if (skip.has(schemaName)) { it.skip(testName, testFunction); @@ -51,10 +49,4 @@ describe('build app - Swift', () => { it(testName, testFunction); } }); - - it('fails build with syntax error', async () => { - await generateModels(projectRoot); - await writeFileSync(path.join(projectRoot, 'amplify', 'generated', 'models', 'AmplifyModels.swift'), 'foo\nbar'); - await expect(swiftBuild(projectRoot, { ...config })).rejects.toThrowError(); - }); }); diff --git a/packages/amplify-codegen-e2e-tests/src/__tests__/model-introspection-codegen.test.ts b/packages/amplify-codegen-e2e-tests/src/__tests__/model-introspection-codegen.test.ts index fd614ecc..18c4d796 100644 --- a/packages/amplify-codegen-e2e-tests/src/__tests__/model-introspection-codegen.test.ts +++ b/packages/amplify-codegen-e2e-tests/src/__tests__/model-introspection-codegen.test.ts @@ -1,4 +1,4 @@ -import { addApiWithoutSchema, createNewProjectDir, generateModelIntrospection, initJSProjectWithProfile, updateApiSchema } from "@aws-amplify/amplify-codegen-e2e-core"; +import { addApiWithoutSchema, createNewProjectDir, generateModelIntrospection, initJSProjectWithProfile, updateApiSchema, generateModelIntrospectionWithError } from "@aws-amplify/amplify-codegen-e2e-core"; import { deleteAmplifyProject } from '../codegen-tests-base'; import { isNotEmptyDir } from "../utils"; import { join } from 'path'; @@ -35,7 +35,7 @@ describe('Model Introspection Codegen test', () => { await addApiWithoutSchema(projectRoot, { apiName }); await updateApiSchema(projectRoot, apiName, schema); //generate introspection schema - await expect(generateModelIntrospection(projectRoot)).rejects.toThrowError(); + await generateModelIntrospectionWithError(projectRoot, 'Expected --output-dir flag to be set'); }); it('should throw error if the GraphQL schema is invalid', async () => { @@ -46,7 +46,7 @@ describe('Model Introspection Codegen test', () => { await updateApiSchema(projectRoot, apiName, invalidSchema); const outputDir = 'output'; //generate introspection schema - await expect(generateModelIntrospection(projectRoot, { outputDir })).rejects.toThrowError(); + await generateModelIntrospectionWithError(projectRoot, 'Unknown type', { outputDir }); }); it(`should handle a schema with connected PK`, async () => { diff --git a/packages/amplify-codegen-e2e-tests/src/configure_tests.ts b/packages/amplify-codegen-e2e-tests/src/configure_tests.ts index 46b6332f..9d04364f 100644 --- a/packages/amplify-codegen-e2e-tests/src/configure_tests.ts +++ b/packages/amplify-codegen-e2e-tests/src/configure_tests.ts @@ -1,4 +1,4 @@ -import { amplifyConfigure as configure, isCI } from '@aws-amplify/amplify-codegen-e2e-core'; +import { amplifyConfigure as configure, injectSessionToken, isCI } from '@aws-amplify/amplify-codegen-e2e-core'; async function setupAmplify() { if (isCI()) { @@ -11,9 +11,13 @@ async function setupAmplify() { await configure({ accessKeyId: AWS_ACCESS_KEY_ID, secretAccessKey: AWS_SECRET_ACCESS_KEY, - profileName: 'e2e-test-user', + profileName: 'amplify-integ-test-user', region: REGION, }); + + if (process.env.AWS_SESSION_TOKEN) { + injectSessionToken('amplify-integ-test-user'); + } } else { console.log('AWS Profile is already configured'); @@ -27,4 +31,5 @@ process.nextTick(async () => { console.log(e.stack); process.exit(1); } + process.exit(); }); diff --git a/scripts/split-e2e-tests-codebuild.ts b/scripts/split-e2e-tests-codebuild.ts index 624df27f..7f32cbde 100644 --- a/scripts/split-e2e-tests-codebuild.ts +++ b/scripts/split-e2e-tests-codebuild.ts @@ -165,13 +165,14 @@ const splitTests = ( tmp.env.variables.USE_PARENT_ACCOUNT = 1; } if (j.runSolo) { - tmp.env['compute-type'] = 'BUILD_GENERAL1_SMALL'; + tmp.env['compute-type'] = 'BUILD_GENERAL1_MEDIUM'; } result.push(tmp); } }); return result; }; + function main(): void { const configBase: any = loadConfigBase(); const baseBuildGraph = configBase.batch['build-graph']; @@ -180,7 +181,7 @@ function main(): void { identifier: 'run_e2e_tests', buildspec: '.codebuild/run_e2e_tests.yml', env: { - 'compute-type': 'BUILD_GENERAL1_MEDIUM', + 'compute-type': 'BUILD_GENERAL1_LARGE', }, 'depend-on': ['publish_to_local_registry'], }, @@ -193,7 +194,7 @@ function main(): void { identifier: 'cleanup_e2e_resources', buildspec: '.codebuild/cleanup_e2e_resources.yml', env: { - 'compute-type': 'BUILD_GENERAL1_SMALL' + 'compute-type': 'BUILD_GENERAL1_MEDIUM' }, 'depend-on': [allBuilds[0].identifier] }