chore: use multi-stage builds for server images in both development and production environments (#2117)

This commit is contained in:
Nico Miguelino
2024-11-08 21:59:42 -08:00
committed by GitHub
parent 293e7a0cd0
commit c766045f3e
14 changed files with 70 additions and 11316 deletions

View File

@@ -69,13 +69,15 @@ jobs:
- name: Build Containers
run: |
poetry run python tools/image_builder \
--dockerfiles-only \
--disable-cache-mounts \
--service celery \
--service redis \
--service test
- name: Start the test container
run: |
docker compose -f docker-compose.test.yml up -d
docker compose -f docker-compose.test.yml up -d --build
- name: Run the unit tests inside the container
run: |

5
.gitignore vendored
View File

@@ -56,3 +56,8 @@ db.sqlite3
# Django
staticfiles/
# Static files
static/js/anthias.js*
static/js/settings.js*
static/css/anthias.css*

View File

@@ -48,6 +48,10 @@ EOF
if [ "$START_SERVER" = true ]; then
cd /usr/src/app
npm install && \
npm run coffee-build && \
npm run sass-build
./manage.py makemigrations
./manage.py migrate --fake-initial
./manage.py runserver 127.0.0.1:8080 &

View File

@@ -27,6 +27,9 @@ fi
if [[ "$ENVIRONMENT" == "development" ]]; then
echo "Starting Django development server..."
npm install && \
npm run coffee-build && \
npm run sass-build
./manage.py runserver 0.0.0.0:8080
else
echo "Generating Django static files..."

View File

@@ -1,3 +1,28 @@
{% if environment == 'production' %}
FROM debian:bookworm as node-builder
{% if disable_cache_mounts %}
RUN \
{% else %}
RUN --mount=type=cache,target=/var/cache/apt \
{% endif %}
apt-get update && \
apt-get -y install --no-install-recommends \
nodejs \
npm
RUN mkdir -p /app
WORKDIR /app
COPY package.json package-lock.json /app
RUN npm install
COPY ./static/js/*.coffee /app/static/js/
COPY ./static/sass/*.scss /app/static/sass/
RUN npm run coffee-build && \
npm run sass-build
{% endif %}
{% include 'Dockerfile.base.j2' %}
COPY requirements/requirements.txt /tmp/requirements.txt
@@ -12,6 +37,15 @@ RUN mkdir -p /usr/src/app
COPY . /usr/src/app/
WORKDIR /usr/src/app
{% if environment == 'production' %}
COPY --from=node-builder \
/app/static/css/ \
/usr/src/app/static/css/
COPY --from=node-builder \
/app/static/js/*.js* \
/usr/src/app/static/js/
{% endif %}
ENV GIT_HASH={{ git_hash }}
ENV GIT_SHORT_HASH={{ git_short_hash }}
ENV GIT_BRANCH={{ git_branch }}

View File

@@ -159,30 +159,34 @@ We've also provided a [checklist](/docs/qa-checklist.md) that can serve as a gui
## Generating CSS and JS files
Anthias only supports compiling from the host container at the moment. You need to install the latest version
of Node.js. We recommend to intall Node.js on Linux. You can use this [guide](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs)
to get started.
To get started, you need to start the development server first. See this [section](#dockerized-development-environment)
for details.
### Installing Node.js dependencies
Run the following command from the project root directory.
```bash
$ npm install
$ docker compose -f docker-compose.dev.yml exec anthias-server \
npm install
```
### Transpiling CSS from SASS
Open a new terminal session and run the following command:
```bash
$ npm run sass-dev
$ docker compose -f docker-compose.dev.yml exec anthias-server \
npm run sass-dev
```
### Transpiling JS from CoffeeScript
Open a new terminal session and run the following command:
```bash
# You need to run this on a separate terminal session if you already ran the
# script for transpiling SASS files.
$ npm run coffee-dev
$ docker compose -f docker-compose.dev.yml exec anthias-server \
npm run coffee-dev
```
### Closing the transpiler

View File

@@ -3,7 +3,9 @@
"version": "1.0.0",
"scripts": {
"coffee-dev": "coffee --compile --map --watch static/js/*.coffee",
"sass-dev": "sass --watch static/sass/:static/css/"
"sass-dev": "sass --watch static/sass/:static/css/",
"coffee-build": "coffee --compile --map static/js/*.coffee",
"sass-build": "sass static/sass/:static/css/"
},
"dependencies": {
"bootstrap": "^4.3.1",

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,138 +0,0 @@
// Generated by CoffeeScript 1.12.7
(function() {
$().ready(function() {
var toggle_chunk;
$("#request-error .close").click(function(e) {
return $("#request-error .alert").hide();
});
$("#btn-backup").click(function(e) {
var btnText;
btnText = $("#btn-backup").text();
$("#btn-backup").text("Preparing archive...");
$("#btn-upload").prop("disabled", true);
$("#btn-backup").prop("disabled", true);
return $.ajax({
method: "POST",
url: "/api/v1/backup",
timeout: 1800 * 1000
}).done(function(data, e) {
if (data) {
return window.location = "/static_with_mime/" + data + "?mime=application/x-tgz";
}
}).fail(function(data, e) {
var err, j;
$("#request-error .alert").addClass("alert-danger");
$("#request-error .alert").removeClass("alert-success");
$("#request-error .alert").show();
if ((data.responseText !== "") && (j = $.parseJSON(data.responseText)) && (err = j.error)) {
return ($("#request-error .msg")).text("Server Error: " + err);
} else {
return ($("#request-error .msg")).text("The operation failed. Please reload the page and try again.");
}
}).always(function(data, e) {
$("#btn-backup").text(btnText);
$("#btn-upload").prop("disabled", false);
return $("#btn-backup").prop("disabled", false);
});
});
$("#btn-upload").click(function(e) {
e.preventDefault();
return $("[name='backup_upload']").click();
});
$("[name='backup_upload']").fileupload({
url: "/api/v1/recover",
progressall: function(e, data) {
var valuenow;
if (data.loaded && data.total) {
valuenow = data.loaded / data.total * 100;
$(".progress .bar").css("width", valuenow + "%");
return $(".progress .bar").text("Uploading: " + Math.floor(valuenow) + "%");
}
},
add: function(e, data) {
$("#btn-upload").hide();
$("#btn-backup").hide();
$(".progress").show();
return data.submit();
},
done: function(e, data) {
var message;
if ((data.jqXHR.responseText !== "") && (message = $.parseJSON(data.jqXHR.responseText))) {
$("#request-error .alert").show();
$("#request-error .alert").addClass("alert-success");
$("#request-error .alert").removeClass("alert-danger");
return ($("#request-error .msg")).text(message);
}
},
fail: function(e, data) {
var err, j;
$("#request-error .alert").show();
$("#request-error .alert").addClass("alert-danger");
$("#request-error .alert").removeClass("alert-success");
if ((data.jqXHR.responseText !== "") && (j = $.parseJSON(data.jqXHR.responseText)) && (err = j.error)) {
return ($("#request-error .msg")).text("Server Error: " + err);
} else {
return ($("#request-error .msg")).text("The operation failed. Please reload the page and try again.");
}
},
always: function(e, data) {
$(".progress").hide();
$("#btn-upload").show();
return $("#btn-backup").show();
}
});
$("#btn-reboot-system").click(function(e) {
if (confirm("Are you sure you want to reboot your device?")) {
return $.post("/api/v1/reboot").done(function(e) {
($("#request-error .alert")).show();
($("#request-error .alert")).addClass("alert-success");
($("#request-error .alert")).removeClass("alert-danger");
return ($("#request-error .msg")).text("Reboot has started successfully.");
}).fail(function(data, e) {
var err, j;
($("#request-error .alert")).show();
($("#request-error .alert")).addClass("alert-danger");
($("#request-error .alert")).removeClass("alert-success");
if ((data.responseText !== "") && (j = $.parseJSON(data.responseText)) && (err = j.error)) {
return ($("#request-error .msg")).text("Server Error: " + err);
} else {
return ($("#request-error .msg")).text("The operation failed. Please reload the page and try again.");
}
});
}
});
$("#btn-shutdown-system").click(function(e) {
if (confirm("Are you sure you want to shutdown your device?")) {
return $.post("/api/v1/shutdown").done(function(e) {
($("#request-error .alert")).show();
($("#request-error .alert")).addClass("alert-success");
($("#request-error .alert")).removeClass("alert-danger");
return ($("#request-error .msg")).text("Device shutdown has started successfully. Soon you will be able to unplug the power from your Raspberry Pi.");
}).fail(function(data, e) {
var err, j;
($("#request-error .alert")).show();
($("#request-error .alert")).addClass("alert-danger");
($("#request-error .alert")).removeClass("alert-success");
if ((data.responseText !== "") && (j = $.parseJSON(data.responseText)) && (err = j.error)) {
return ($("#request-error .msg")).text("Server Error: " + err);
} else {
return ($("#request-error .msg")).text("The operation failed. Please reload the page and try again.");
}
});
}
});
toggle_chunk = function() {
$("[id^=auth_chunk]").hide();
return $.each($('#auth_backend option'), function(e, t) {
return $('#auth_backend-' + t.value).toggle($('#auth_backend').val() === t.value);
});
};
$('#auth_backend').change(function(e) {
return toggle_chunk();
});
return toggle_chunk();
});
}).call(this);
//# sourceMappingURL=settings.js.map

View File

File diff suppressed because one or more lines are too long

View File

@@ -282,6 +282,8 @@ def build_image(
'libcups2',
'libxcomposite1',
'libxdamage1',
'nodejs',
'npm',
],
'chrome_dl_url': chrome_dl_url,
'chromedriver_dl_url': chromedriver_dl_url,
@@ -334,10 +336,9 @@ def build_image(
],
'archive_url': archive_url,
})
elif service == 'nginx':
context.update({
'environment': environment,
})
elif service == 'server':
if environment == 'development':
base_apt_dependencies.extend(['nodejs', 'npm'])
generate_dockerfile(service, {
'base_image': base_image,
@@ -346,6 +347,7 @@ def build_image(
'board': board,
'debian_version': 'bookworm',
'disable_cache_mounts': disable_cache_mounts,
'environment': environment,
'git_branch': git_branch,
'git_hash': git_hash,
'git_short_hash': git_short_hash,