Skip to content

Commit d7df3d3

Browse files
authored
Merge pull request #5 from StackExchange/docker
feat: created automation to containerise demo app
2 parents 4c31a2a + 33d5333 commit d7df3d3

File tree

7 files changed

+472
-8
lines changed

7 files changed

+472
-8
lines changed

.dockerignore

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
.git
2-
.yarn/cache
3-
.yarn/install-state.gz
1+
dist-types
42
node_modules
5-
packages/*/src
3+
packages/*/dist
64
packages/*/node_modules
7-
plugins
8-
*.local.yaml
5+
packages/backend/.env
6+
plugins/*/dist
7+
plugins/*/node_modules
8+
*.local.yaml
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: "Build and Publish Docker Demo TEST"
2+
3+
on:
4+
workflow_dispatch:
5+
6+
env:
7+
REGISTRY_IMAGE_BASE: estoesmoises/stackoverflow-backstage-demo
8+
IMAGE_TAG: test
9+
10+
jobs:
11+
build-multi-arch:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
platform:
17+
- linux/amd64
18+
- linux/arm64
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
23+
- name: Prepare
24+
run: |
25+
platform=${{ matrix.platform }}
26+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
27+
28+
- name: Login to Docker Hub
29+
uses: docker/login-action@v3
30+
with:
31+
username: ${{ secrets.DOCKER_USERNAME }}
32+
password: ${{ secrets.DOCKER_PASSWORD }}
33+
34+
- name: Set up QEMU
35+
uses: docker/setup-qemu-action@v3
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Build and push by digest
41+
id: build
42+
uses: docker/build-push-action@v6
43+
with:
44+
context: .
45+
platforms: ${{ matrix.platform }}
46+
outputs: type=image,push-by-digest=true,name=${{ env.REGISTRY_IMAGE_BASE }},push=true
47+
48+
- name: Export digest
49+
run: |
50+
mkdir -p ${{ runner.temp }}/digests
51+
digest="${{ steps.build.outputs.digest }}"
52+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
53+
54+
- name: Upload digest
55+
uses: actions/upload-artifact@v4
56+
with:
57+
name: digests-${{ env.PLATFORM_PAIR }}
58+
path: ${{ runner.temp }}/digests/*
59+
if-no-files-found: error
60+
retention-days: 1
61+
62+
merge:
63+
runs-on: ubuntu-latest
64+
needs:
65+
- build-multi-arch
66+
steps:
67+
- name: Download digests
68+
uses: actions/download-artifact@v4
69+
with:
70+
path: ${{ runner.temp }}/digests
71+
pattern: digests-*
72+
merge-multiple: true
73+
74+
- name: Login to Docker Hub
75+
uses: docker/login-action@v3
76+
with:
77+
username: ${{ secrets.DOCKER_USERNAME }}
78+
password: ${{ secrets.DOCKER_PASSWORD }}
79+
80+
- name: Set up Docker Buildx
81+
uses: docker/setup-buildx-action@v3
82+
83+
- name: Create manifest list and push with test tag
84+
working-directory: ${{ runner.temp }}/digests
85+
run: |
86+
docker buildx imagetools create -t ${{ env.REGISTRY_IMAGE_BASE }}:${{ env.IMAGE_TAG }} \
87+
$(printf '${{ env.REGISTRY_IMAGE_BASE }}@sha256:%s ' *)
88+
89+
- name: Inspect image
90+
run: |
91+
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE_BASE }}:${{ env.IMAGE_TAG }}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: "Build and Publish Docker Demo"
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
env:
9+
REGISTRY_IMAGE: estoesmoises/stackoverflow-backstage-demo
10+
11+
jobs:
12+
build-multi-arch:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
platform:
18+
- linux/amd64
19+
- linux/arm64
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Prepare
25+
run: |
26+
platform=${{ matrix.platform }}
27+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
28+
29+
- name: Docker meta
30+
id: meta
31+
uses: docker/metadata-action@v5
32+
with:
33+
images: ${{ env.REGISTRY_IMAGE }}
34+
35+
- name: Login to Docker Hub
36+
uses: docker/login-action@v3
37+
with:
38+
username: ${{ secrets.DOCKER_USERNAME }}
39+
password: ${{ secrets.DOCKER_PASSWORD }}
40+
41+
- name: Set up QEMU
42+
uses: docker/setup-qemu-action@v3
43+
44+
- name: Set up Docker Buildx
45+
uses: docker/setup-buildx-action@v3
46+
47+
- name: Build and push by digest
48+
id: build
49+
uses: docker/build-push-action@v6
50+
with:
51+
context: .
52+
platforms: ${{ matrix.platform }}
53+
labels: ${{ steps.meta.outputs.labels }}
54+
outputs: type=image,push-by-digest=true,name=${{ env.REGISTRY_IMAGE }},push=true
55+
56+
- name: Export digest
57+
run: |
58+
mkdir -p ${{ runner.temp }}/digests
59+
digest="${{ steps.build.outputs.digest }}"
60+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
61+
62+
- name: Upload digest
63+
uses: actions/upload-artifact@v4
64+
with:
65+
name: digests-${{ env.PLATFORM_PAIR }}
66+
path: ${{ runner.temp }}/digests/*
67+
if-no-files-found: error
68+
retention-days: 1
69+
70+
merge:
71+
runs-on: ubuntu-latest
72+
needs:
73+
- build-multi-arch
74+
steps:
75+
- name: Download digests
76+
uses: actions/download-artifact@v4
77+
with:
78+
path: ${{ runner.temp }}/digests
79+
pattern: digests-*
80+
merge-multiple: true
81+
82+
- name: Login to Docker Hub
83+
uses: docker/login-action@v3
84+
with:
85+
username: ${{ secrets.DOCKER_USERNAME }}
86+
password: ${{ secrets.DOCKER_PASSWORD }}
87+
88+
- name: Set up Docker Buildx
89+
uses: docker/setup-buildx-action@v3
90+
91+
- name: Docker meta
92+
id: meta
93+
uses: docker/metadata-action@v5
94+
with:
95+
images: ${{ env.REGISTRY_IMAGE }}
96+
tags: |
97+
type=semver,pattern={{version}}
98+
type=semver,pattern={{major}}.{{minor}}
99+
type=raw,value=latest,enable=${{ github.ref_type == 'tag' }}
100+
101+
- name: Create manifest list and push
102+
working-directory: ${{ runner.temp }}/digests
103+
run: |
104+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
105+
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
106+
107+
- name: Inspect image
108+
run: |
109+
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}

Dockerfile

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# DO NOT USE THIS IN PRODUCTION
2+
#
3+
# This Dockerfile builds a Docker image for a pre-configured Backstage instance
4+
# with the Stack Overflow plugins installed. It’s designed as a quick way to try
5+
# out the integration without having to set it up yourself.
6+
#
7+
# This is not intended for production use.
8+
9+
# Stage 1 - Create yarn install skeleton layer
10+
FROM node:20-bookworm-slim AS packages
11+
12+
WORKDIR /app
13+
COPY backstage.json package.json yarn.lock ./
14+
COPY .yarn ./.yarn
15+
COPY .yarnrc.yml ./
16+
17+
COPY packages packages
18+
19+
# Comment this out if you don't have any internal plugins
20+
COPY plugins plugins
21+
22+
RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -exec rm -rf {} \+
23+
24+
# Stage 2 - Install dependencies and build packages
25+
FROM node:20-bookworm-slim AS build
26+
27+
# Set Python interpreter for `node-gyp` to use
28+
ENV PYTHON=/usr/bin/python3
29+
30+
# Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend.
31+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
32+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
33+
apt-get update && \
34+
apt-get install -y --no-install-recommends python3 g++ build-essential && \
35+
rm -rf /var/lib/apt/lists/*
36+
37+
# Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image,
38+
# in which case you should also move better-sqlite3 to "devDependencies" in package.json.
39+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
40+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
41+
apt-get update && \
42+
apt-get install -y --no-install-recommends libsqlite3-dev && \
43+
rm -rf /var/lib/apt/lists/*
44+
45+
USER node
46+
WORKDIR /app
47+
48+
COPY --from=packages --chown=node:node /app .
49+
50+
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
51+
yarn install --immutable
52+
53+
COPY --chown=node:node . .
54+
55+
RUN yarn tsc
56+
RUN yarn --cwd packages/backend build
57+
58+
RUN mkdir packages/backend/dist/skeleton packages/backend/dist/bundle \
59+
&& tar xzf packages/backend/dist/skeleton.tar.gz -C packages/backend/dist/skeleton \
60+
&& tar xzf packages/backend/dist/bundle.tar.gz -C packages/backend/dist/bundle
61+
62+
# Stage 3 - Build the actual backend image and install production dependencies
63+
FROM node:20-bookworm-slim
64+
65+
# Set Python interpreter for `node-gyp` to use
66+
ENV PYTHON=/usr/bin/python3
67+
68+
# Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend.
69+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
70+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
71+
apt-get update && \
72+
apt-get install -y --no-install-recommends python3 g++ build-essential && \
73+
rm -rf /var/lib/apt/lists/*
74+
75+
# Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image,
76+
# in which case you should also move better-sqlite3 to "devDependencies" in package.json.
77+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
78+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
79+
apt-get update && \
80+
apt-get install -y --no-install-recommends libsqlite3-dev && \
81+
rm -rf /var/lib/apt/lists/*
82+
83+
# From here on we use the least-privileged `node` user to run the backend.
84+
USER node
85+
86+
# This should create the app dir as `node`.
87+
# If it is instead created as `root` then the `tar` command below will
88+
# fail: `can't create directory 'packages/': Permission denied`.
89+
# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`)
90+
# so the app dir is correctly created as `node`.
91+
WORKDIR /app
92+
93+
# Copy the install dependencies from the build stage and context
94+
COPY --from=build --chown=node:node /app/.yarn ./.yarn
95+
COPY --from=build --chown=node:node /app/.yarnrc.yml ./
96+
COPY --from=build --chown=node:node /app/backstage.json ./
97+
COPY --from=build --chown=node:node /app/yarn.lock /app/package.json /app/packages/backend/dist/skeleton/ ./
98+
99+
# Note: The skeleton bundle only includes package.json files -- if your app has
100+
# plugins that define a `bin` export, the bin files need to be copied as well to
101+
# be linked in node_modules/.bin during yarn install.
102+
103+
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
104+
yarn workspaces focus --all --production && rm -rf "$(yarn cache clean)"
105+
106+
# Copy the built packages from the build stage
107+
COPY --from=build --chown=node:node /app/packages/backend/dist/bundle/ ./
108+
109+
# Copy any other files that we need at runtime
110+
COPY --chown=node:node app-config*.yaml ./
111+
112+
# This will include the examples, if you don't need these simply remove this line
113+
COPY --chown=node:node examples ./examples
114+
115+
# This switches many Node.js dependencies to production mode.
116+
ENV NODE_ENV=development
117+
118+
# This disables node snapshot for Node 20 to work with the Scaffolder
119+
ENV NODE_OPTIONS="--no-node-snapshot"
120+
121+
CMD ["node", "packages/backend", "--config", "app-config.docker-local.yaml"]

0 commit comments

Comments
 (0)