From 42c562c76ab0b31fb7129d3f681f608a5cfe3660 Mon Sep 17 00:00:00 2001 From: Nick Oliver Date: Wed, 13 Sep 2023 12:07:10 -0700 Subject: [PATCH] feat(Dockerfile): use .nvmrc to build the image (#1090) and run workflows, creating a single point to define the Node.js version to use and thus making upgrades easier Co-authored-by: Michael Rochester Co-authored-by: Danny Co-authored-by: Jonny Adshead --- .github/workflows/README.md | 2 - .github/workflows/bundle-size.yml | 2 +- ...-and-trunk_one-app-unit-and-lint-tests.yml | 2 +- .github/workflows/on-pr_dangerJS.yml | 2 +- .../on-pr_one-app-integration-tests.yml | 4 +- ...elease-step-1_manual_create-release-pr.yml | 2 +- ...utomatic_docker-prod-build-and-publish.yml | 2 +- ...automatic_docker-dev-build-and-publish.yml | 2 +- ...tomatic_publish-one-app-statics-to-npm.yml | 2 +- .nvmrc | 1 + Dockerfile | 7 +-- __tests__/package.spec.js | 46 ++++++++++++++++--- docs/guides/Running-In-Production.md | 4 +- 13 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 .nvmrc diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 2b1a24906..5f4a98609 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -26,8 +26,6 @@ Runs CodeQL against the PR code ### [on PR, Trunk] One App Unit and Lint Tests Runs One App's unit tests and linting tests against the PR code -This workflow creates two checks, one for node 14 and one for node 16. - ### [on PR] DangerJS Runs dangerJS against the PR code diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 66ac98d09..8e001585f 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16.x + node-version-file: .nvmrc - uses: preactjs/compressed-size-action@v2 env: NODE_ENV: development diff --git a/.github/workflows/on-pr-and-trunk_one-app-unit-and-lint-tests.yml b/.github/workflows/on-pr-and-trunk_one-app-unit-and-lint-tests.yml index 52b24167e..dc9ac5e13 100644 --- a/.github/workflows/on-pr-and-trunk_one-app-unit-and-lint-tests.yml +++ b/.github/workflows/on-pr-and-trunk_one-app-unit-and-lint-tests.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x, "^18 < 18.16"] + node-version: [16.x, 18.x] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/on-pr_dangerJS.yml b/.github/workflows/on-pr_dangerJS.yml index 39f7f9b89..3a043526a 100644 --- a/.github/workflows/on-pr_dangerJS.yml +++ b/.github/workflows/on-pr_dangerJS.yml @@ -8,7 +8,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version-file: .nvmrc - uses: actions/cache@v3 with: path: ~/.npm diff --git a/.github/workflows/on-pr_one-app-integration-tests.yml b/.github/workflows/on-pr_one-app-integration-tests.yml index 47526b74f..2e0f7a052 100644 --- a/.github/workflows/on-pr_one-app-integration-tests.yml +++ b/.github/workflows/on-pr_one-app-integration-tests.yml @@ -14,10 +14,10 @@ jobs: ${{ runner.os }}-node- - uses: actions/setup-node@v3 with: - node-version: 16.x + node-version-file: .nvmrc - name: npm install run: NODE_ENV=development npm ci - name: Build docker image - run: docker build -t one-app:at-test . --build-arg USER=root + run: docker build -t one-app:at-test . --build-arg USER=root --build-arg VERSION=$(cat .nvmrc) - name: Run Integration Tests run: ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD=true npm run test:integration diff --git a/.github/workflows/release-step-1_manual_create-release-pr.yml b/.github/workflows/release-step-1_manual_create-release-pr.yml index bd292005e..0acf38807 100644 --- a/.github/workflows/release-step-1_manual_create-release-pr.yml +++ b/.github/workflows/release-step-1_manual_create-release-pr.yml @@ -28,7 +28,7 @@ jobs: ${{ runner.os }}-node- - uses: actions/setup-node@v3 with: - node-version: 16.x + node-version-file: .nvmrc - name: One App release id: vars run: | diff --git a/.github/workflows/release-step-4_automatic_docker-prod-build-and-publish.yml b/.github/workflows/release-step-4_automatic_docker-prod-build-and-publish.yml index 6efd8bc91..9830d18e9 100644 --- a/.github/workflows/release-step-4_automatic_docker-prod-build-and-publish.yml +++ b/.github/workflows/release-step-4_automatic_docker-prod-build-and-publish.yml @@ -73,7 +73,7 @@ jobs: - name: Docker login run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login --username ${{ secrets.DOCKER_USER }} --password-stdin - name: Build production docker image - run: docker build -t prod . + run: docker build -t prod . --build-arg VERSION=$(cat .nvmrc) - name: Tag Docker Images run: | # Always tag the exact version diff --git a/.github/workflows/release-step-5_automatic_docker-dev-build-and-publish.yml b/.github/workflows/release-step-5_automatic_docker-dev-build-and-publish.yml index 8d68d452a..aed59b593 100644 --- a/.github/workflows/release-step-5_automatic_docker-dev-build-and-publish.yml +++ b/.github/workflows/release-step-5_automatic_docker-dev-build-and-publish.yml @@ -68,7 +68,7 @@ jobs: - name: Docker login run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login --username ${{ secrets.DOCKER_USER }} --password-stdin - name: Build development docker image - run: docker build -t dev . --target=development + run: docker build -t dev . --target=development --build-arg VERSION=$(cat .nvmrc) - name: Tag Docker Images run: | # Always tag the exact version diff --git a/.github/workflows/release-step-6-automatic_publish-one-app-statics-to-npm.yml b/.github/workflows/release-step-6-automatic_publish-one-app-statics-to-npm.yml index af4643661..ba17d8736 100644 --- a/.github/workflows/release-step-6-automatic_publish-one-app-statics-to-npm.yml +++ b/.github/workflows/release-step-6-automatic_publish-one-app-statics-to-npm.yml @@ -27,7 +27,7 @@ jobs: # Setup .npmrc file to publish to npm - uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version-file: .nvmrc registry-url: 'https://registry.npmjs.org' - name: Docker login run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login --username ${{ secrets.DOCKER_USER }} --password-stdin diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..4a1f488b6 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.17.1 diff --git a/Dockerfile b/Dockerfile index dd6fed9fd..8e93ac8b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ +ARG VERSION=lts # Use the pre-baked fat node image only in the builder # which includes build utils preinstalled (e.g. gcc, make, etc). # This will result in faster and reliable One App docker image # builds as we do not have to run apk installs for alpine. -FROM node:18.17.1 as builder +FROM node:$VERSION as builder WORKDIR /opt/build RUN npm install -g npm@9.6.7 --registry=https://registry.npmjs.org COPY --chown=node:node ./ /opt/build @@ -29,7 +30,7 @@ RUN NODE_ENV=production npm run build && \ # development image # docker build . --target=development -FROM node:18.17.1-alpine as development +FROM node:$VERSION-alpine as development ARG USER ENV USER ${USER:-node} ENV NODE_ENV=development @@ -47,7 +48,7 @@ COPY --from=builder --chown=node:node /opt/one-app/development ./ # production image # last so that it's the default image artifact -FROM node:18.17.1-alpine as production +FROM node:$VERSION-alpine as production ARG USER ENV USER ${USER:-node} ENV NODE_ENV=production diff --git a/__tests__/package.spec.js b/__tests__/package.spec.js index 1acad1ce5..b1f0317d7 100644 --- a/__tests__/package.spec.js +++ b/__tests__/package.spec.js @@ -13,16 +13,50 @@ * or implied. See the License for the specific language governing * permissions and limitations under the License. */ - -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs/promises'; +import path from 'node:path'; const PACKAGE_DIR_PATH = path.resolve(path.join(__dirname, '../')); describe('package.json', () => { - it('is formatted properly', () => { - const rawPkg = fs.readFileSync(path.join(PACKAGE_DIR_PATH, 'package.json'), 'utf8'); + it('is parseable', async () => { + expect.assertions(1); + const rawPkg = await fs.readFile(path.join(PACKAGE_DIR_PATH, 'package.json'), 'utf8'); + expect(() => JSON.parse(rawPkg)).not.toThrow(); + }); + it('is formatted like npm does', async () => { + expect.assertions(1); + const rawPkg = await fs.readFile(path.join(PACKAGE_DIR_PATH, 'package.json'), 'utf8'); const parsedPkg = JSON.parse(rawPkg); - expect(rawPkg).toEqual(`${JSON.stringify(parsedPkg, null, 2)}\n`); + const builtPkg = { ...parsedPkg }; + + [ + 'dependencies', + 'devDependencies', + 'optionalDependencies', + ].forEach((listName) => { + if (!Object.hasOwnProperty.call(parsedPkg, listName)) { + return; + } + builtPkg[listName] = Object + .entries(parsedPkg[listName]) + .sort(([packageNameA], [packageNameB]) => ( + // let the engine do the work of sorting the strings + [packageNameA, packageNameB].sort()[0] === packageNameA ? -1 : 1) + ) + // ECMAScript Objects do not have an order to their keys, but the V8 implementation does + .reduce( + (orderedList, [packageName, versionRange]) => { + // adding properties one at a time is less bad than rebuilding (via destructuring) a + // new object every iteration of the loop + /* eslint-disable-next-line no-param-reassign */ + orderedList[packageName] = versionRange; + return orderedList; + }, + {} + ); + }); + + expect(rawPkg).toEqual(`${JSON.stringify(builtPkg, null, 2)}\n`); }); }); diff --git a/docs/guides/Running-In-Production.md b/docs/guides/Running-In-Production.md index e2390f373..b731eb9b1 100644 --- a/docs/guides/Running-In-Production.md +++ b/docs/guides/Running-In-Production.md @@ -140,7 +140,7 @@ You can build the One App [Docker](https://www.docker.com/) image and run it in ```bash git clone https://github.com/americanexpress/one-app.git cd one-app -docker build . +docker build . --build-arg VERSION=$(cat .nvmrc) ``` Or you can build from source which creates your server side assets at `./lib` and your client @@ -217,6 +217,6 @@ under the "Software" category click "Edit". Enter all the required environment v After the deployment is complete, you can navigate to your application by clicking on "Go to environment". If the application health displays an error, One App might be failing to start due to a missing environment variable or missing configuration. You can request the logs and the console should display the reason why One App is crashing. -More information on how to deploy Docker containers on AWS Elastic Beanstalk can be found on the official [AWS Documentation](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker.html). +More information on how to deploy Docker containers on AWS Elastic Beanstalk can be found on the official [AWS Documentation](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker.html). [☝️ Return To Top](#running-in-production)