diff --git a/.github/actions/run-full-stack/action.yml b/.github/actions/run-full-stack/action.yml index da18810477..924d9c2b89 100644 --- a/.github/actions/run-full-stack/action.yml +++ b/.github/actions/run-full-stack/action.yml @@ -9,5 +9,5 @@ runs: run: | set -x export JWT_PRIVATE_KEY="${{ env.JWT_PRIVATE_KEY }}" - docker compose -f docker-compose.yml down -v - docker compose -f docker-compose.yml up db data-import backend frontend-static --build -d + docker compose -f docker-compose.static.yml down -v + docker compose -f docker-compose.static.yml up --build -d diff --git a/.github/workflows/nightly_scans.yml b/.github/workflows/nightly_scans.yml index 9f69e06247..03b8c2b23d 100644 --- a/.github/workflows/nightly_scans.yml +++ b/.github/workflows/nightly_scans.yml @@ -34,6 +34,6 @@ jobs: uses: SvanBoxel/zaproxy-to-ghas@cfc77481d74a17a4c3d6b753aa9d7abef453d501 # v1.0.2 - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@4f3212b61783c3c68e8309a0f18a699764811cda # v3 + uses: github/codeql-action/upload-sarif@9278e421667d5d90a2839487a482448c4ec7df4d # v3 with: sarif_file: results.sarif diff --git a/.github/workflows/security_codeql.yml b/.github/workflows/security_codeql.yml index e43702ba51..d1d9385a9f 100644 --- a/.github/workflows/security_codeql.yml +++ b/.github/workflows/security_codeql.yml @@ -22,7 +22,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3 + uses: github/codeql-action/init@9278e421667d5d90a2839487a482448c4ec7df4d # v3 with: languages: javascript, python # If you wish to specify custom queries, you can do so here or in a config file. @@ -33,4 +33,4 @@ jobs: queries: +security-extended - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3 + uses: github/codeql-action/analyze@9278e421667d5d90a2839487a482448c4ec7df4d # v3 diff --git a/.github/workflows/security_semgrep.yml b/.github/workflows/security_semgrep.yml index 3c416e3d49..c4c7d969d2 100644 --- a/.github/workflows/security_semgrep.yml +++ b/.github/workflows/security_semgrep.yml @@ -25,7 +25,7 @@ jobs: SEMGREP_RULES: "p/default" - name: Upload SARIF file for GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@4f3212b61783c3c68e8309a0f18a699764811cda # v3 + uses: github/codeql-action/upload-sarif@9278e421667d5d90a2839487a482448c4ec7df4d # v3 with: sarif_file: ${{ env.SEMGREP_TO_UPLOAD }} if: always() diff --git a/README.md b/README.md index fbe319f902..cbe9868a2a 100644 --- a/README.md +++ b/README.md @@ -87,19 +87,13 @@ docker compose up --build To run the application using the production server configuration... ```shell -docker compose up db data-import backend frontend-static --build +docker compose -f docker-compose.static.yml up --build ```` -To run the application using the minimal initial data set... - -```shell - docker compose --profile data-initial up --build -``` - To run the application using the demo data set... ```shell - docker compose --profile data-demo up --build +docker compose -f docker-compose.demo.yml up --build ``` diff --git a/backend/data_tools/tests/docker-compose.yml b/backend/data_tools/tests/docker-compose.yml index acda2ed160..1382ca0f11 100644 --- a/backend/data_tools/tests/docker-compose.yml +++ b/backend/data_tools/tests/docker-compose.yml @@ -4,7 +4,6 @@ services: db: image: "postgres:16" - platform: linux/amd64 container_name: unit-test-db security_opt: - no-new-privileges:true # Resolve semgrep https://sg.run/0n8q diff --git a/backend/ops_api/tests/docker-compose.yml b/backend/ops_api/tests/docker-compose.yml index 95cc9c6cac..1e18698e51 100644 --- a/backend/ops_api/tests/docker-compose.yml +++ b/backend/ops_api/tests/docker-compose.yml @@ -1,7 +1,6 @@ services: unittest_db: image: "postgres:16" - platform: linux/amd64 container_name: unit-test-db command: -c 'max_connections=400' security_opt: @@ -24,7 +23,6 @@ services: build: context: ../../../backend dockerfile: Dockerfile.data-tools - platform: linux/amd64 container_name: pytest-data-import environment: - ENV=pytest diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml new file mode 100644 index 0000000000..d1ea53dbbd --- /dev/null +++ b/docker-compose.demo.yml @@ -0,0 +1,90 @@ +services: + + db: + image: "postgres:16" + container_name: ops-db + security_opt: + - no-new-privileges:true # Resolve semgrep https://sg.run/0n8q + environment: + - POSTGRES_PASSWORD=local_password + read_only: true # Resolve semgrep https://sg.run/e4JE + tmpfs: /var/run/postgresql/ + volumes: + - ./backend/data_tools/ops_db_sql_init:/docker-entrypoint-initdb.d + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + + disable-users: + build: + context: ./backend/ + dockerfile: Dockerfile.data-tools + container_name: disable-users + environment: + - ENV=local + - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres + command: ["/home/app/.venv/bin/python", "./data_tools/src/disable_users/disable_users.py"] + depends_on: + db: + condition: service_healthy + data-import: + condition: service_completed_successfully + + frontend: + build: + context: ./frontend/ + dockerfile: Dockerfile + environment: + - REACT_APP_BACKEND_DOMAIN=http://localhost:8080 + - VITE_BACKEND_DOMAIN=http://localhost:8080 + container_name: ops-frontend-demo + ports: + - "3000:3000" + depends_on: + - backend + volumes: + - ./frontend/src:/home/app/src + + backend: + build: + context: ./backend/ + dockerfile: Dockerfile.ops-api + container_name: ops-backend-demo + ports: + - "8080:8080" + command: /bin/sh -c " . .venv/bin/activate && python -m flask run --debug --host=0.0.0.0 --port=8080" + environment: + - JWT_PRIVATE_KEY + - JWT_PUBLIC_KEY + - OPS_CONFIG=environment/local/container.py + volumes: + - ./backend/ops_api/ops:/home/app/ops_api/ops + depends_on: + db: + condition: service_healthy + data-import: + condition: service_completed_successfully + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080" ] + interval: 10s + timeout: 10s + retries: 10 + + data-import: + build: + context: ./backend/ + dockerfile: Dockerfile.data-tools + container_name: ops-data-demo + environment: + - ENV=local + - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres + command: /bin/sh -c "./data_tools/scripts/import_test_data.sh && ./data_tools/scripts/demo_data.sh" + volumes: + - ./backend/ops_api:/home/app/ops_api + depends_on: + db: + condition: service_healthy diff --git a/docker-compose.static.yml b/docker-compose.static.yml new file mode 100644 index 0000000000..2250c7f241 --- /dev/null +++ b/docker-compose.static.yml @@ -0,0 +1,91 @@ +services: + + db: + image: "postgres:16" + container_name: ops-db + security_opt: + - no-new-privileges:true # Resolve semgrep https://sg.run/0n8q + environment: + - POSTGRES_PASSWORD=local_password + read_only: true # Resolve semgrep https://sg.run/e4JE + tmpfs: /var/run/postgresql/ + volumes: + - ./backend/data_tools/ops_db_sql_init:/docker-entrypoint-initdb.d + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + + data-import: + build: + context: ./backend/ + dockerfile: Dockerfile.data-tools + container_name: ops-data-import + environment: + - ENV=local + - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres + command: /bin/sh -c "./data_tools/scripts/import_test_data.sh" + volumes: + # See below for an explanation of this volume. The same reasoning applies, + # but in this case it's so we can run new migrations immediately without + # having to rebuild the migration container. + - ./backend/ops_api:/home/app/ops_api + depends_on: + db: + condition: service_healthy + + disable-users: + build: + context: ./backend/ + dockerfile: Dockerfile.data-tools + container_name: disable-users + environment: + - ENV=local + - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres + command: ["/home/app/.venv/bin/python", "./data_tools/src/disable_users/disable_users.py"] + depends_on: + db: + condition: service_healthy + data-import: + condition: service_completed_successfully + + backend: + build: + context: ./backend/ + dockerfile: Dockerfile.ops-api + container_name: ops-backend + ports: + - "8080:8080" + command: /bin/sh -c " . .venv/bin/activate && python -m flask run --debug --host=0.0.0.0 --port=8080" + environment: + - JWT_PRIVATE_KEY + - JWT_PUBLIC_KEY + - OPS_CONFIG=environment/local/container.py + volumes: + - ./backend/ops_api/ops:/home/app/ops_api/ops + depends_on: + db: + condition: service_healthy + data-import: + condition: service_completed_successfully + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080" ] + interval: 10s + timeout: 10s + retries: 10 + + frontend-static: + build: + context: ./frontend/ + dockerfile: Dockerfile.azure + args: + VITE_BACKEND_DOMAIN: http://localhost:8080 + MODE: dev # set this to production to create a production build + container_name: ops-frontend + ports: + - "3000:3000" + depends_on: + - backend diff --git a/docker-compose.yml b/docker-compose.yml index cdbebf39cc..548c221cdc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,7 @@ services: db: - profiles: - - '' # This is the default profile, which is used for development - - data-initial # This profile is used to initialize the database with initial data - - data-demo # This profile is used to initialize the database with demo data image: "postgres:16" - platform: linux/amd64 container_name: ops-db security_opt: - no-new-privileges:true # Resolve semgrep https://sg.run/0n8q @@ -25,12 +20,9 @@ services: retries: 5 data-import: - profiles: - - '' # This is the default profile, which is used for development build: context: ./backend/ dockerfile: Dockerfile.data-tools - platform: linux/amd64 container_name: ops-data-import environment: - ENV=local @@ -46,12 +38,9 @@ services: condition: service_healthy disable-users: - profiles: - - '' # This is the default profile, which is used for development build: context: ./backend/ dockerfile: Dockerfile.data-tools - platform: linux/amd64 container_name: disable-users environment: - ENV=local @@ -64,12 +53,9 @@ services: condition: service_completed_successfully backend: - profiles: - - '' # This is the default profile, which is used for development build: context: ./backend/ dockerfile: Dockerfile.ops-api - platform: linux/amd64 container_name: ops-backend ports: - "8080:8080" @@ -92,12 +78,9 @@ services: retries: 10 frontend: - profiles: - - '' # This is the default profile, which is used for development build: context: ./frontend/ dockerfile: Dockerfile - platform: linux/amd64 container_name: ops-frontend environment: - REACT_APP_BACKEND_DOMAIN=http://localhost:8080 @@ -108,151 +91,3 @@ services: - backend volumes: - ./frontend/src:/home/app/src - - frontend-static: - profiles: - - static - build: - context: ./frontend/ - dockerfile: Dockerfile.azure - args: - VITE_BACKEND_DOMAIN: http://localhost:8080 - MODE: dev # set this to production to create a production build - platform: linux/amd64 - container_name: ops-frontend-static - ports: - - "3000:3000" - depends_on: - - backend - - - # The following services are used to initialize the database with initial data using the data-initial profile. - - frontend-static-initial: - profiles: - - data-initial - build: - context: ./frontend/ - dockerfile: Dockerfile.azure - args: - VITE_BACKEND_DOMAIN: http://localhost:8080 - MODE: dev # set this to production to create a production build - platform: linux/amd64 - container_name: ops-frontend-static-initial - ports: - - "3000:3000" - depends_on: - - backend-initial - - - backend-initial: - profiles: - - data-initial - build: - context: ./backend/ - dockerfile: Dockerfile.ops-api - platform: linux/amd64 - container_name: ops-backend-initial - ports: - - "8080:8080" - command: /bin/sh -c " . .venv/bin/activate && python -m flask run --debug --host=0.0.0.0 --port=8080" - environment: - - JWT_PRIVATE_KEY - - JWT_PUBLIC_KEY - - OPS_CONFIG=environment/local/container.py - volumes: - - ./backend/ops_api/ops:/home/app/ops_api/ops - depends_on: - db: - condition: service_healthy - data-initial: - condition: service_completed_successfully - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8080" ] - interval: 10s - timeout: 10s - retries: 10 - - data-initial: - profiles: - - data-initial - build: - context: ./backend/ - dockerfile: Dockerfile.data-tools - platform: linux/amd64 - container_name: ops-data-initial - environment: - - ENV=local - - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres - command: /bin/sh -c "./data_tools/scripts/initial_data.sh" - volumes: - - ./backend/ops_api:/home/app/ops_api - depends_on: - db: - condition: service_healthy - - # The following services are used to initialize the database with demo data using the data-demo profile. - - frontend-demo: - profiles: - - data-demo - build: - context: ./frontend/ - dockerfile: Dockerfile - environment: - - REACT_APP_BACKEND_DOMAIN=http://localhost:8080 - - VITE_BACKEND_DOMAIN=http://localhost:8080 - platform: linux/amd64 - container_name: ops-frontend-demo - ports: - - "3000:3000" - depends_on: - - backend-demo - volumes: - - ./frontend/src:/home/app/src - - backend-demo: - profiles: - - data-demo - build: - context: ./backend/ - dockerfile: Dockerfile.ops-api - platform: linux/amd64 - container_name: ops-backend-demo - ports: - - "8080:8080" - command: /bin/sh -c " . .venv/bin/activate && python -m flask run --debug --host=0.0.0.0 --port=8080" - environment: - - JWT_PRIVATE_KEY - - JWT_PUBLIC_KEY - - OPS_CONFIG=environment/local/container.py - volumes: - - ./backend/ops_api/ops:/home/app/ops_api/ops - depends_on: - db: - condition: service_healthy - data-demo: - condition: service_completed_successfully - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8080" ] - interval: 10s - timeout: 10s - retries: 10 - - data-demo: - profiles: - - data-demo - build: - context: ./backend/ - dockerfile: Dockerfile.data-tools - platform: linux/amd64 - container_name: ops-data-demo - environment: - - ENV=local - - SQLALCHEMY_DATABASE_URI=postgresql://ops:ops@db:5432/postgres - command: /bin/sh -c "./data_tools/scripts/import_test_data.sh && ./data_tools/scripts/demo_data.sh" - volumes: - - ./backend/ops_api:/home/app/ops_api - depends_on: - db: - condition: service_healthy diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 89c401619a..d36bfd05e2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -7,7 +7,7 @@ WORKDIR /home/app COPY --chown=app:app ./package.json ./bun.lockb /home/app/ -RUN bun install +RUN bun install --production --frozen-lockfile COPY --chown=app:app index.html /home/app/ COPY --chown=app:app src /home/app/src