diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 24a1a4e..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,195 +0,0 @@ -version: 2.1 - -executors: - amd64: - machine: - image: ubuntu-2404:2024.05.1 - resource_class: medium - arm64: - machine: - image: ubuntu-2404:2024.05.1 - resource_class: arm.medium - -platforms: &platforms - - amd64 - - arm64 - -node_versions: &node_versions - - "18" - - "20" - - "22" - -workflows: - build_test: - jobs: - - build: - stream: test - push: false - context: docker-publishing - matrix: - parameters: - executor: *platforms - node_version: *node_versions - filters: - branches: - ignore: [ main, releases ] - - build_stable: - jobs: - - build: - stream: stable - context: docker-publishing - matrix: - parameters: - executor: *platforms - node_version: *node_versions - filters: - branches: - only: [ releases ] - - manifest: - stream: stable - context: docker-publishing - requires: [ build ] - matrix: - parameters: - node_version: *node_versions - filters: - branches: - only: [ releases ] - - build_latest: - jobs: - - build: - stream: latest - context: docker-publishing - matrix: - parameters: - executor: *platforms - node_version: *node_versions - filters: - branches: - only: [ main ] - - manifest: - stream: latest - context: docker-publishing - requires: [ build ] - matrix: - parameters: - node_version: *node_versions - filters: - branches: - only: [ main ] - - nightly_stable: - jobs: - - build: - stream: stable - context: docker-publishing - matrix: - parameters: - executor: *platforms - node_version: *node_versions - - manifest: - stream: stable - context: docker-publishing - requires: [ build ] - matrix: - parameters: - node_version: *node_versions - filters: - branches: - only: [ releases ] - triggers: - - schedule: - # Scheduled build for 2am AEST nightly. - cron: "0 15 * * *" - filters: - branches: - only: [ releases ] - - nightly_latest: - jobs: - - build: - stream: latest - context: docker-publishing - matrix: - parameters: - executor: *platforms - node_version: *node_versions - - manifest: - stream: latest - context: docker-publishing - requires: [ build ] - matrix: - parameters: - node_version: *node_versions - filters: - branches: - only: [ main ] - triggers: - - schedule: - # Scheduled build for 2am AEST nightly. - cron: "0 15 * * *" - filters: - branches: - only: [ main ] - -jobs: - build: - parameters: - executor: - type: string - node_version: - type: string - push: - type: boolean - default: true - stream: - type: string - executor: << parameters.executor >> - steps: - - checkout - - run: - name: "Install: Container Structure Tests" - command: | - curl -LO https://storage.googleapis.com/container-structure-test/v1.11.0/container-structure-test-linux-<< parameters.executor >> && \ - mv container-structure-test-linux-<< parameters.executor >> container-structure-test && \ - chmod +x container-structure-test && \ - sudo mv container-structure-test /usr/local/bin/ - - run: - name: Build, Test and Release - command: | - SHA1_VERSION="v3-$(git rev-parse --short HEAD)" - make build ARCH=<< parameters.executor >> NODE_VERSION=<< parameters.node_version >> VERSION_TAG=v3-<< parameters.stream >> - make build ARCH=<< parameters.executor >> NODE_VERSION=<< parameters.node_version >> VERSION_TAG=${SHA1_VERSION} - - when: - condition: - equal: [ true, << parameters.push >> ] - steps: - - run: - name: "Push image" - command: | - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - SHA1_VERSION="v3-$(git rev-parse --short HEAD)" - make push ARCH=<< parameters.executor >> NODE_VERSION=<< parameters.node_version >> VERSION_TAG=v3-<< parameters.stream >> - make push ARCH=<< parameters.executor >> NODE_VERSION=<< parameters.node_version >> VERSION_TAG=${SHA1_VERSION} - manifest: - docker: - - image: cimg/base:2024.09 - parameters: - node_version: - type: string - stream: - type: string - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: true - - run: - name: Push Manifest - command: | - export DOCKER_CLI_EXPERIMENTAL=enabled - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - SHA1_VERSION="v3-$(git rev-parse --short HEAD)" - make manifest NODE_VERSION=<< parameters.node_version >> VERSION_TAG=v3-<< parameters.stream >> - make manifest NODE_VERSION=<< parameters.node_version >> VERSION_TAG=${SHA1_VERSION} diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml new file mode 100644 index 0000000..ee10cb7 --- /dev/null +++ b/.github/workflows/build-pr.yml @@ -0,0 +1,29 @@ +name: 🏗️ Build PR + +on: + pull_request: ~ + +env: + stream: "pr-${{ github.event.pull_request.number }}" + +jobs: + build: + name: Build Docker image + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [ '20', '22', '24' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: 🐋 Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: 🏗️ Build Docker image + uses: docker/bake-action@v6 + env: + NODE_VERSION: ${{ matrix.node }} + STREAM: ${{ env.stream }} diff --git a/.github/workflows/build-push-latest.yml b/.github/workflows/build-push-latest.yml new file mode 100644 index 0000000..e49f3c2 --- /dev/null +++ b/.github/workflows/build-push-latest.yml @@ -0,0 +1,17 @@ +name: 🏗️ Build and Push Latest + +on: + push: + branches: [ main ] + workflow_dispatch: ~ + schedule: + # At 14:00 UTC every day + - cron: '0 14 * * *' + +jobs: + build: + uses: ./.github/workflows/build-push.yml + with: + stream: latest + push: true + secrets: inherit diff --git a/.github/workflows/build-push-stable.yml b/.github/workflows/build-push-stable.yml new file mode 100644 index 0000000..3ddd240 --- /dev/null +++ b/.github/workflows/build-push-stable.yml @@ -0,0 +1,17 @@ +name: 🏗️ Build and Push Stable + +on: + push: + branches: [ releases ] + workflow_dispatch: ~ + schedule: + # At 14:00 UTC every day + - cron: '0 14 * * *' + +jobs: + build: + uses: ./.github/workflows/build-push.yml + with: + stream: stable + push: true + secrets: inherit diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml new file mode 100644 index 0000000..dfe0b86 --- /dev/null +++ b/.github/workflows/build-push.yml @@ -0,0 +1,62 @@ +name: 🏗️ Build and Push + +on: + workflow_call: + inputs: + runs_on: + type: string + default: ubuntu-latest + description: The image to run the jobs. + stream: + type: string + default: latest + description: The stream to build (e.g latest or stable). + push: + type: boolean + default: false + description: Whether to push the built image to the registry. + + secrets: + DOCKERHUB_USERNAME: + required: true + DOCKERHUB_TOKEN: + required: true + +jobs: + build: + name: Build Docker image + runs-on: ${{ inputs.runs_on }} + permissions: + packages: write + contents: read + attestations: write + id-token: write + strategy: + fail-fast: false + matrix: + node: [ '20', '22', '24' ] + + steps: + - name: 🔑 Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: 🔑 Log in to the GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: 🐋 Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: 🏗️ Build and push Docker image + uses: docker/bake-action@v6 + env: + PHP_VERSION: ${{ matrix.php }} + STREAM: ${{ inputs.stream }} + with: + push: ${{ inputs.push }} diff --git a/Dockerfile b/Dockerfile index c6f12af..09554b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ -ARG FROM_IMAGE -FROM ${FROM_IMAGE} +FROM from_image AS base # Libuv 1.45.0 is affected by a kernel bug on certain kernels. # This leads to errors where Garden tool downloading errors with ETXTBSY @@ -49,4 +48,13 @@ RUN npm install -g pnpm@10 USER skpr -ENV PATH /data/node_modules/.bin:$PATH +ENV PATH=/data/node_modules/.bin:$PATH + +# Temporary build stage where we can run the test suite. +FROM base AS test +COPY --from=ghcr.io/goss-org/goss:latest /usr/bin/goss /usr/bin/goss +ADD goss.yml /tmp/goss.yml +RUN goss --gossfile=/tmp/goss.yml validate + +FROM base AS run +CMD ["bash"] diff --git a/Makefile b/Makefile index f9c74d1..e3528a2 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,10 @@ #!/usr/bin/make -f -REGISTRY=skpr/node -ALPINE_VERSION=3.21 -NODE_VERSION=20 -ARCH=amd64 -VERSION_TAG=v3-latest +NODE_VERSION=22 -IMAGE=${REGISTRY}:${NODE_VERSION}-${VERSION_TAG} -IMAGE_DEV=${REGISTRY}:dev-${NODE_VERSION}-${VERSION_TAG} - -build: - # Building production image. - docker build --build-arg FROM_IMAGE=node:${NODE_VERSION}-alpine${ALPINE_VERSION} -t ${IMAGE}-${ARCH} . - # Building development image. - docker build --build-arg FROM_IMAGE=${IMAGE}-${ARCH} -t ${IMAGE_DEV}-${ARCH} dev - # Testing development image. - container-structure-test test --image ${IMAGE_DEV}-${ARCH} --config tests.yml - -push: - # Pushing production image. - docker push ${IMAGE}-${ARCH} - # Pushing development image. - docker push ${IMAGE_DEV}-${ARCH} - -manifest: - # Creating manifest for production image. - docker manifest create ${IMAGE} --amend ${IMAGE}-arm64 --amend ${IMAGE}-amd64 - docker manifest push ${IMAGE} - # Creating manifest for development image. - docker manifest create ${IMAGE_DEV} --amend ${IMAGE_DEV}-arm64 --amend ${IMAGE_DEV}-amd64 - docker manifest push ${IMAGE_DEV} +# Example build command for local development. +# See Github Action for multi-arch and multi-stream building. +nbake: + NODE_VERSION=${NODE_VERSION} docker buildx bake .PHONY: * diff --git a/README.md b/README.md index a9dd439..be50dab 100644 --- a/README.md +++ b/README.md @@ -7,31 +7,36 @@ Images for building and running Node applications. This image suite provides 2 streams for images: -* `stable` - A stable upstream. +* `stable` - Our production/stable upstream for projects. Use this by default. * `latest` - Recently merged changes which will be merged into `stable` as part of a release. ## Images -**Stable** +Images are available in the following registries: -``` -docker.io/skpr/node:22-v3-stable -docker.io/skpr/node:20-v3-stable -docker.io/skpr/node:18-v3-stable +* `ghcr.io` +* `docker.io` -docker.io/skpr/node:dev-22-v3-stable -docker.io/skpr/node:dev-20-v3-stable -docker.io/skpr/node:dev-18-v3-stable -``` +## Image List + +Below is the list of Node images we provide. -**Latest** +By default we recommend the following registry and stream: ``` -docker.io/skpr/node:22-v3-latest +REGISTRY=docker.io +STREAM=stable +``` + +``` +docker.io/skpr/node:24-v3-STREAM +docker.io/skpr/node:22-v3-STREAM docker.io/skpr/node:20-v3-latest -docker.io/skpr/node:18-v3-latest +``` -docker.io/skpr/node:dev-22-v3-latest -docker.io/skpr/node:dev-20-v3-latest -docker.io/skpr/node:dev-18-v3-latest +## Building + +You need to specify `NODE_VERSION` to build locally: +``` +make build NODE_VERSION=24 ``` diff --git a/dev/Dockerfile b/dev/Dockerfile index e219239..c33514c 100644 --- a/dev/Dockerfile +++ b/dev/Dockerfile @@ -1,5 +1,4 @@ -ARG FROM_IMAGE -FROM ${FROM_IMAGE} +FROM from_image USER root RUN apk add --no-cache \ diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..7975dd2 --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,84 @@ +variable "NODE_VERSION" { + default = "22" +} + +variable "ALPINE_VERSION" { + default = "3.21" +} + +variable "STREAM" { + default = "latest" +} + +variable "VERSION" { + default = "v3" +} + +variable "PLATFORMS" { + type = list(string) + default = [ + "linux/amd64", + "linux/arm64", + ] +} + +variable "REGISTRIES" { + default = ["docker.io", "ghcr.io"] +} + +# Common target: Everything inherits from this +target "_common" { + platforms = PLATFORMS +} + +group "default" { + targets = [ + "prod", + "dev", + "test", + ] +} + +target "prod" { + inherits = ["_common"] + context = "." + + contexts = { + from_image = "docker-image://node:${NODE_VERSION}-alpine${ALPINE_VERSION}" + } + + tags = [ + for r in REGISTRIES : + "${r}/skpr/node:${NODE_VERSION}-${VERSION}-${STREAM}" + ] +} + +target "dev" { + inherits = ["_common"] + context = "dev" + + contexts = { + from_image = "target:prod" + } + + tags = [ + for r in REGISTRIES : + "${r}/skpr/node:dev-${NODE_VERSION}-${VERSION}-${STREAM}" + ] +} + +target "test" { + matrix = { + variant = ["prod",] + } + + name = "${variant}-test" + + inherits = [variant] + + # Run this stage from the Dockerfile. + target = "test" + + # Only build the test target locally. + output = ["type=cacheonly"] +} diff --git a/goss.yml b/goss.yml new file mode 100644 index 0000000..23ae89f --- /dev/null +++ b/goss.yml @@ -0,0 +1,18 @@ +command: + npm: + exec: "npm --help" + exit-status: 1 + stdout: + - DISCLAIMER + + yarn: + exec: "yarn --help" + exit-status: 0 + stdout: + - DISCLAIMER + + pnpm: + exec: "pnpm --help" + exit-status: 0 + stdout: + - "compiled to binary" diff --git a/tests.yml b/tests.yml deleted file mode 100644 index 4b6856f..0000000 --- a/tests.yml +++ /dev/null @@ -1,20 +0,0 @@ -schemaVersion: '2.0.0' - -commandTests: - - name: "npm" - command: "npm" - args: ["--help"] - expectedOutput: ["DISCLAIMER"] - exitCode: 1 - - - name: "yarn" - command: "yarn" - args: ["--help"] - expectedOutput: ["DISCLAIMER"] - exitCode: 0 - - - name: "pnpm" - command: "pnpm" - args: ["--help"] - expectedOutput: ["compiled to binary"] - exitCode: 0