Skip to content

Commit 73aa4bd

Browse files
committed
feat: Provide helm chart
1 parent cf6f9b1 commit 73aa4bd

37 files changed

+2913
-67
lines changed

.github/actions/determine-target-image-tags/action

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ echo "MAJOR_VERSION=${MAJOR_VERSION}"
1414
test -n "${VERSION}" || ( echo "VERSION is not specified" && false )
1515
test -n "${MAJOR_VERSION}" || ( echo "MAJOR_VERSION is not specified" && false )
1616

17-
git fetch --tags
18-
1917
GLOBAL_LATEST_VERSION="$(git-semver latest --include-pre-releases)"
2018
echo "GLOBAL_LATEST_VERSION=${GLOBAL_LATEST_VERSION}"
2119

.github/workflows/bump-version.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
name: Bump Version
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v4
1313
- uses: PSanetra/git-semver-actions/next@v1
1414
id: next_version
1515
- uses: PSanetra/git-semver-actions/latest@v1

.github/workflows/main.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
name: On Tag pushed
1+
name: Test and Push Images on new Tag
2+
23
on:
34
push:
45
tags:
56
- v*
67

78
jobs:
8-
test:
9+
push_images:
910
name: Test and Push Images
1011
runs-on: ubuntu-latest
1112
strategy:
1213
matrix:
1314
# "{0}" will be replaced by the latest pushed nginx version
1415
nginx: [ "mainline", "stable", "{0}" ]
1516
steps:
16-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1718
with:
1819
ref: ${{ github.ref }}
20+
fetch-depth: 0
21+
fetch-tags: true
22+
1923
- name: Get latest nginx tag
2024
id: latest_nginx_tag
2125
uses: ./.github/actions/latest-docker-repository-version
@@ -39,7 +43,7 @@ jobs:
3943
git-ref: "${{ github.ref }}"
4044
nginx-tag: "${{ steps.target_nginx_tag.outputs.tag }}"
4145
matrix-nginx: "${{ matrix.nginx }}"
42-
docker-repository: "${{ secrets.DOCKER_REPOSITORY }}"
46+
docker-repository: "${{ vars.DOCKER_REPOSITORY }}"
4347
- uses: docker/setup-qemu-action@v2
4448
name: Set up QEMU
4549
- uses: docker/setup-buildx-action@v2
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Test and Push Helm Chart on new Tag
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
jobs:
9+
test_and_push_helm_chart:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
16+
- name: Get Latest Semantic Version
17+
id: get_latest_version
18+
uses: PSanetra/git-semver-actions/latest@v1
19+
20+
- name: Set up Helm
21+
uses: azure/setup-helm@v3
22+
23+
- name: Install Helm Unittest Plugin
24+
run: helm plugin install https://github.com/helm-unittest/helm-unittest
25+
26+
- name: Run Helm Lint
27+
run: helm lint chart --strict
28+
29+
- name: Run Helm Unit Tests
30+
run: helm unittest chart --strict
31+
32+
- name: Package Helm Chart
33+
run: helm package chart -u --version ${{ steps.get_latest_version.outputs.version }} --destination .
34+
35+
- name: Install yq
36+
run: sudo snap install yq
37+
38+
- name: Push Helm Chart to OCI Registry
39+
run: |
40+
echo '${{ secrets.DOCKER_PASSWORD }}' | helm registry login ${{ vars.HELM_REGISTRY }} --username '${{ secrets.DOCKER_USERNAME }}' --password-stdin
41+
helm push $(yq -r '.name' ./chart/Chart.yaml | tr -d '\n')-${{ steps.get_latest_version.outputs.version }}.tgz ${{ vars.HELM_REPOSITORY }}

.github/workflows/update.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Update Docker Images
1+
name: Update Images
22
on:
33
push:
44
branches:
@@ -17,7 +17,7 @@ jobs:
1717
# "{0}" will be replaced by the latest pushed nginx version
1818
nginx: [ "mainline", "stable", "{0}" ]
1919
steps:
20-
- uses: actions/checkout@v2
20+
- uses: actions/checkout@v4
2121
- name: Get latest nginx tag
2222
id: latest_nginx_tag
2323
uses: ./.github/actions/latest-docker-repository-version
@@ -33,12 +33,12 @@ jobs:
3333
id: target_image_name
3434
run: |
3535
IMAGE_TAG="${{ matrix.spa_server_major_version }}-nginx-${{ steps.target_nginx_tag.outputs.tag }}"
36-
IMAGE_NAME="${{ secrets.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
36+
IMAGE_NAME="${{ vars.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
3737
echo "::set-output name=image_name::${IMAGE_NAME}"
3838
echo "IMAGE_NAME=${IMAGE_NAME}"
39-
TAGS="${{ secrets.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
39+
TAGS="${{ vars.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
4040
if [ "latest" = "${{ matrix.spa_server_major_version }}" ] && [ "stable" = "${{ matrix.nginx }}" ]; then
41-
TAGS="${TAGS},${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.spa_server_major_version }},${{ secrets.DOCKER_REPOSITORY }}:latest"
41+
TAGS="${TAGS},${{ vars.DOCKER_REPOSITORY }}:${{ matrix.spa_server_major_version }},${{ vars.DOCKER_REPOSITORY }}:latest"
4242
fi
4343
echo "::set-output name=tags::${TAGS}"
4444
echo "TAGS=${TAGS}"
@@ -58,7 +58,7 @@ jobs:
5858
run: sudo chmod -R ugo+rwX .
5959
if: steps.check_if_update_is_necessary.outputs.needs_update == 'true'
6060
- name: Checkout latest SPA-Server version for major version ${{ matrix.spa_server_major_version }}
61-
uses: actions/checkout@v2
61+
uses: actions/checkout@v4
6262
with:
6363
ref: "v${{ steps.latest_release.outputs.version }}"
6464
if: steps.check_if_update_is_necessary.outputs.needs_update == 'true'

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
/config/.out
33
/config_tests/.tmp
44
/tests/target
5+
*.tgz

README.md

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# Single Page Application Server
2+
23
![Update Docker Images](https://github.com/codecentric/single-page-application-server/workflows/Update%20Docker%20Images/badge.svg)
34

4-
This image can be used as a base image for single page applications. It is itself based on Nginx.
5+
This container image provides a base for serving Single Page Applications (SPAs), leveraging Nginx as its web server.
6+
7+
For a fitting Helm Chart see [chart/README.md](https://github.com/codecentric/single-page-application-server/blob/master/chart/README.md).
58

69
## Tags
710

8-
The following tags will be updated automatically with the latest nginx base image on a weekly basis:
11+
The following tags are updated automatically on a weekly basis with the latest Nginx base image:
912

1013
* `latest` (alias for `1-nginx-stable-alpine`)
1114
* `1` (alias for `1-nginx-stable-alpine`)
@@ -14,54 +17,73 @@ The following tags will be updated automatically with the latest nginx base imag
1417
* `latest-nginx-mainline-alpine` (alias for `1-nginx-mainline-alpine`)
1518
* `1-nginx-mainline-alpine`
1619

17-
There will also be tags for specific versions.
20+
Additional tags for specific Nginx versions are also available.
1821

1922
## Examples
2023

2124
Examples for usage with Angular and React are located in the `examples` directory.
2225

2326
## General Features
2427

25-
* Any non-existing routes return the root `index.html`
26-
* Does NOT apply to resource routes with filename extensions such as: `js | css | ico | pdf | flv | jpg | jpeg | png | gif | swf`
27-
* Configure application dynamically at container startup
28-
* Configure base element
29-
* Support environment specific configuration depending on the requested host (port and domain)
30-
* Hashed resources are cached indefinitely by the browser without revalidating
31-
* Resource name needs to look like `my-script.3f8a240b.js` or `my-script.3f8a240b.chunk.js`
32-
* Hash needs to consist of at least 8 characters
33-
* Applies to resources with filename extensions such as: `js | css | ico | pdf | flv | jpg | jpeg | png | gif | swf`
34-
* HTTP 2 is enabled by default for HTTPS connections
28+
- **SPA Routes Handling**: Routes not matching static files will serve `index.html`, with exceptions for resources like `.js`, `.css`, etc.
29+
- **Dynamic Configuration**: Configure applications at container startup.
30+
- **Environment-Specific Config**: Customize settings based on port and domain.
31+
- **Resource Caching**: Hashed resources are cached indefinitely. Resources must include a hash of at least 8 characters.
32+
- **HTTP/2**: Enabled by default for HTTPS connections.
33+
- **Helm Chart**: A general [Helm chart](https://github.com/codecentric/single-page-application-server/blob/master/chart/README.md) is available for applications using this image.
3534

3635
## Security Features
3736

38-
* Restrictive Content Security Policy by default
39-
* Server API endpoints can be whitelisted automatically
40-
* Referrer is disabled by default
41-
* Content Type Sniffing is disabled by default
42-
* HTTPS is enforced via HSTS by default if enabled
43-
* HTTPS uses [recommended OWASP protocols and cipher suites](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)
44-
* Container runs as non-root user
45-
* Nevertheless the server can still bind to port 80 and 443
46-
* Serving source map files (*.js.map, *.css.map) is disabled by default
37+
- **Content Security Policy**: Restrictive by default, with automatic whitelisting for server API endpoints.
38+
- **Referrer Policy**: Disabled by default to prevent leakage.
39+
- **Content Type Sniffing**: Disabled by default.
40+
- **HTTPS**: Enforced via HSTS if enabled; uses [recommended OWASP protocols and cipher suites.](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)
41+
- **Non-Root User**: The container runs as a non-root user but can bind to ports 80 and 443.
42+
- **Source Maps**: Disabled by default.
43+
- **Read-only root filesystem**: [Supported at container runtime](#read-only-root-filesystem-support)
4744

4845
## Configuration
4946

5047
### App Directory
5148

52-
Copy your SPA resources to `/app/`. All resources in this directory will be served by the Nginx server.
49+
Place your SPA resources in `/app/`. All files in this directory will be served by Nginx.
5350

5451
### YAML Configuration
5552

56-
The application container is configured via YAML files at startup time.
53+
Configure the application through YAML files at startup:
5754

58-
If you need to configure some default settings for your application image, you can add a default configuration file to `/config/default.yaml`.
55+
1. **Default Configuration**: Add a default configuration file to `/config/default.yaml`. Usually added during `docker build`.
56+
2. **Runtime Configuration**: Mount a runtime configuration file at `/config/config.yaml`. This file will override default settings.
5957

60-
To configure settings at runtime, you can mount your runtime configuration file at `/config/config.yaml`. Every specified setting in this file will override the default setting.
58+
#### Example Configuration
6159

62-
It is also possible to merge multiple configuration files by specifying the `CONFIG_FILES` environment variable like `CONFIG_FILES="file:///config/config1.yaml|file:///config/config2.yaml"`. The configuration options, specified in the first configuration file in that variable, will have priority over the options in later declared files.
60+
```yaml
61+
default:
62+
spa_config:
63+
appTitle: "My Application"
64+
endpoints:
65+
api: "https://api.example.com"
66+
```
67+
68+
You can also define host-specific configurations:
69+
70+
```yaml
71+
default:
72+
spa_config:
73+
appTitle: "My Default Title"
74+
endpoints:
75+
api: "https://api.example.com"
76+
special_host:
77+
server_names:
78+
- "special.example.com"
79+
spa_config:
80+
appTitle: "My Domain-specific Title"
81+
```
82+
83+
#### Configuration Reference
6384
6485
The following configuration shows the default values of this base image for every available setting:
86+
6587
```yaml
6688
default:
6789
# Specifies to which host names this configuration should apply.
@@ -132,40 +154,20 @@ default:
132154
style-src: "'self'"
133155
```
134156
135-
Aside to the `default` configuration block, you can also define other blocks, which might define configuration settings for special hosts. The non-default blocks will inherit the configured settings of the default-block if they are not explicitly redeclared.
136-
137-
Example:
138-
```yaml
139-
default:
140-
spa_config:
141-
appTitle: "My Default Application"
142-
endpoints:
143-
globalApi: "https://api.example.com"
144-
special_host:
145-
server_names:
146-
- "special.example.com"
147-
spa_config:
148-
appTitle: "My Special Application"
149-
```
150-
151-
With this configuration the application would have the app title "My Special Application", when it is accessed via the host `special.example.com`, while the endpoints would stay the same in every instance of the application.
152-
153157
## Read-only Root Filesystem Support
154158
155-
It is recommended to use a read-only root filesystem when running containers. However, the following directories must remain writable when using this base image:
159+
For security, use a read-only root filesystem. Ensure the following directories are writable:
156160
157-
* `/config/.out`
158-
* This base image generates files in this directory at startup.
159-
* `/tmp`
160-
* Nginx uses this directory to manage cached files and the nginx.pid file. For more information, see [nginxinc/docker-nginx-unprivileged#troubleshooting-tips](https://github.com/nginxinc/docker-nginx-unprivileged/tree/af6e325d35e6833af9cdda8493866b88649e8aaf?tab=readme-ov-file#troubleshooting-tips).
161+
* `/config/.out`: Used for file generation.
162+
* `/tmp`: Used by Nginx for cached files and `nginx.pid`.
161163

162-
It is possible to mount these directories as writable volumes. When using Kubernetes, one solution is to mount `emptyDir` volumes at these mount points.
164+
When using Kubernetes, consider mounting these directories as writable volumes with `emptyDir`.
163165

164166
## Development
165167

166-
Configuration files are dynamically generated via [gomplate templates](https://docs.gomplate.ca/).
167-
168-
Tests are written in Java using [Testcontainers](https://www.testcontainers.org/).
168+
* **Configuration Generation**: Uses [gomplate templates](https://docs.gomplate.ca/).
169+
* **Image Tests**: Written in Java using [Testcontainers](https://www.testcontainers.org/).
170+
* **Helm Chart Tests**: Uses the [helm-unittest](https://github.com/helm-unittest/helm-unittest) Helm plugin.
169171

170172
## License
171173

chart/.helmignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
24+
# Testing
25+
ci
26+
tests

chart/Chart.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v2
2+
name: single-page-application-server-chart
3+
description: Helm chart for images based on codecentric/single-page-application-server
4+
type: application
5+
version: 0.0.0
6+
maintainers:
7+
- name: Philip Sanetra
8+
9+
sources:
10+
- "https://github.com/codecentric/single-page-application-server"

0 commit comments

Comments
 (0)