From 48e279dee931947ccc7124b4d42e39472eac245a Mon Sep 17 00:00:00 2001 From: Linda Loftsgarden Date: Wed, 17 Apr 2024 19:29:43 +0200 Subject: [PATCH] Setup dockerfile and GHA. --- .github/workflows/deploy.yaml | 147 ++++++++++++++++++ Dockerfile | 13 ++ app/components/app-bar/AppBar.tsx | 17 +- app/root.tsx | 16 +- app/routes/kontroll-admin.define-role.$id.tsx | 4 +- .../kontroll-admin.features-to-role.tsx | 2 +- app/routes/resource-admin.$id.tsx | 2 +- app/routes/resource-module-admin._index.tsx | 4 +- .../resource-module-admin.administer.$id.tsx | 10 +- ...urce-module-admin.opprett-ny-tildeling.tsx | 12 +- app/routes/resources.$id.role-assignments.tsx | 2 +- app/routes/resources.$id.tsx | 2 +- app/routes/resources.$id.user-assignments.tsx | 2 +- app/routes/resources._index.tsx | 4 +- app/routes/roles.$id.assignments.tsx | 2 +- app/routes/roles.$id.members.tsx | 2 +- app/routes/roles.$id.tsx | 2 +- app/routes/roles._index.tsx | 2 +- app/routes/users.$id.tsx | 4 +- app/routes/users._index.tsx | 2 +- package-lock.json | 7 +- package.json | 1 + server/server.js | 20 ++- vite.config.ts | 3 +- 24 files changed, 226 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/deploy.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..62e1546 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,147 @@ +name: Build and deploy + +on: [ push ] + +env: + IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }} + LATEST_IMAGE: ghcr.io/${{ github.repository }}:latest + +jobs: + install: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache node_modules + uses: actions/cache@v4 + id: node_modules + with: + path: ./node_modules + key: modules-${{ hashFiles('package-lock.json') }} + + - uses: actions/setup-node@v4 + if: steps.node_modules.outputs.cache-hit != 'true' + with: + node-version: "20.x" + cache: "npm" + + - run: npm ci --ignore-scripts + if: steps.node_modules.outputs.cache-hit != 'true' + + test: + needs: [ install ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20.x" + + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: ./node_modules + key: modules-${{ hashFiles('package-lock.json') }} + + - run: npm run test + + playwright: + needs: [ install ] + timeout-minutes: 2 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Cache node_modules + uses: actions/cache@v4 + id: node_modules + with: + path: ./node_modules + key: modules-${{ hashFiles('package-lock.json') }} + + - uses: actions/setup-node@v4 + if: steps.node_modules.outputs.cache-hit != 'true' + with: + node-version: "20.x" + cache: "npm" + registry-url: "https://npm.pkg.github.com" + + - name: "Install dependencies" + run: npm ci --ignore-scripts + if: steps.node_modules.outputs.cache-hit != 'true' + + - name: Store Playwright's Version + run: | + PLAYWRIGHT_VERSION=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//') + echo "Playwright's Version: $PLAYWRIGHT_VERSION" + echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV + + - name: Cache Chromium browser for Playwright's Version + id: cache-chromium-browser + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: chromium-with-playwright-version-${{ env.PLAYWRIGHT_VERSION }} + + - name: Install Chromium browser + if: steps.cache-chromium-browser.outputs.cache-hit != 'true' + run: npx playwright install chromium --with-deps + + - name: Run Playwright Tests + run: npm run playwright:verbose + + - name: Store Artifacts from Failed Tests + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: test-results/ + retention-days: 7 + + build: + needs: [ install ] + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20.x" + + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: ./node_modules + key: modules-${{ hashFiles('package-lock.json') }} + + - run: npm run build + + # Bygg docker image + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/build-push-action@v5 + with: + context: . + push: true + pull: true + tags: ${{ env.IMAGE }},${{ env.LATEST_IMAGE }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Cache static files + uses: actions/cache@v4 + with: + path: | + ./build + key: ${{ github.sha }} + + + + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b334162 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app +COPY build/ build/ +COPY server/ server/ +COPY public/ public/ +COPY node_modules/ node_modules/ +COPY package.json ./ +COPY environment.js ./ + +EXPOSE 3000 + +CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/app/components/app-bar/AppBar.tsx b/app/components/app-bar/AppBar.tsx index ae866c0..b7b48f4 100644 --- a/app/components/app-bar/AppBar.tsx +++ b/app/components/app-bar/AppBar.tsx @@ -8,12 +8,12 @@ import { TasklistIcon, XMarkIcon } from '@navikt/aksel-icons'; -import {Link} from "@remix-run/react"; +import { NavLink, Link} from "@remix-run/react"; import MeInfo from "~/components/app-bar/MeInfo"; import {IMeInfo} from "~/data/types"; import {BodyShort, Box, Button, HGrid, Hide, HStack, LinkPanel, Popover} from "@navikt/ds-react"; -export function AppBar(props: { me: IMeInfo }) { +export function AppBar(props: { me: IMeInfo, basePath?: string }) { const buttonRef = useRef(null); const [menuOpen, setMenuOpen] = useState(false); @@ -65,36 +65,37 @@ export function AppBar(props: { me: IMeInfo }) { setMenuOpen(false)}>Til forsiden + - + Brukere - + Grupper - + Ressurser - + Ressursmoduladministrator - + Ressursadministrator - + Kontrolladministrasjon diff --git a/app/root.tsx b/app/root.tsx index 1d332c4..fbef04a 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -10,6 +10,7 @@ import type {LoaderFunctionArgs} from "@remix-run/router"; import {ToastContainer} from "react-toastify"; import {BodyShort, Box, Page} from "@navikt/ds-react"; import {AppBar} from "~/components/app-bar/AppBar"; +import {BASE_PATH} from "../environment"; export const meta: MetaFunction = () => { @@ -41,11 +42,15 @@ export const links: LinksFunction = () => [ export async function loader({request}: LoaderFunctionArgs) { const response = await fetchMeInfo(request.headers.get("Authorization")); - return json(await response.json()); + const me = await response.json(); + return json({ + me, + basePath: BASE_PATH === "/" ? "" : BASE_PATH + }); } export default function App() { - const me = useLoaderData(); + const {me, basePath} = useLoaderData(); return ( @@ -56,7 +61,7 @@ export default function App() { - + @@ -70,9 +75,10 @@ export default function App() { interface LayoutProps { children: any me?: any + basePath?: string } -const Layout = ({children, me}: LayoutProps) => { +const Layout = ({children, me, basePath}: LayoutProps) => { return ( { > - + & { json(): Promise diff --git a/app/routes/users.$id.tsx b/app/routes/users.$id.tsx index c2657db..6a39b94 100644 --- a/app/routes/users.$id.tsx +++ b/app/routes/users.$id.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import UserInfo from "~/components/user/UserInfo"; -import styles from "~/components/user/user.css?url" +import UserInfo from "../components/user/UserInfo"; +import styles from "../components/user/user.css?url" import {Heading} from "@navikt/ds-react"; import {useLoaderData} from "@remix-run/react"; import {IAssignmentPage, IUser} from "~/data/types"; diff --git a/app/routes/users._index.tsx b/app/routes/users._index.tsx index ccabfcf..01c2795 100644 --- a/app/routes/users._index.tsx +++ b/app/routes/users._index.tsx @@ -7,7 +7,7 @@ import {useLoaderData, useSearchParams} from "@remix-run/react"; import {fetchUsers} from "~/data/fetch-users"; import {IUnitItem, IUnitTree, IUserPage} from "~/data/types"; import {LoaderFunctionArgs} from "@remix-run/router"; -import OrgUnitFilterModal from "~/components/org-unit-filter/OrgUnitFilterModal"; +import OrgUnitFilterModal from "../components/org-unit-filter/OrgUnitFilterModal"; import {fetchOrgUnits} from "~/data/fetch-resources"; import {UserTypeFilter} from "~/components/user/UserTypeFilter"; diff --git a/package-lock.json b/package-lock.json index a175ea2..f9ff335 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "dependencies": { "@navikt/ds-css": "^6.5.0", "@navikt/ds-react": "^6.5.0", + "@remix-run/express": "^2.8.1", "@remix-run/node": "^2.8.1", "@remix-run/react": "^2.8.1", "axios": "^1.6.8", @@ -2147,9 +2148,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.8.1.tgz", "integrity": "sha512-p1eo8uwZk8uLihSDpUnPOPsTDfghWikVPQfa+e0ZMk6tnJCjcpHAyENKDFtn9vDh9h7YNUg6A7+19CStHgxd7Q==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "@remix-run/node": "2.8.1" }, @@ -15431,9 +15429,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.8.1.tgz", "integrity": "sha512-p1eo8uwZk8uLihSDpUnPOPsTDfghWikVPQfa+e0ZMk6tnJCjcpHAyENKDFtn9vDh9h7YNUg6A7+19CStHgxd7Q==", - "dev": true, - "optional": true, - "peer": true, "requires": { "@remix-run/node": "2.8.1" } diff --git a/package.json b/package.json index 7250363..68aa332 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dependencies": { "@navikt/ds-css": "^6.5.0", "@navikt/ds-react": "^6.5.0", + "@remix-run/express": "^2.8.1", "@remix-run/node": "^2.8.1", "@remix-run/react": "^2.8.1", "axios": "^1.6.8", diff --git a/server/server.js b/server/server.js index 85372f2..25f72e8 100644 --- a/server/server.js +++ b/server/server.js @@ -1,9 +1,14 @@ import express from "express"; import {BASE_PATH, PORT} from "../environment.js"; import morgan from "morgan"; -import prometheusMiddleware from "express-prometheus-middleware"; import log4js from 'log4js'; -import {createRequestHandler} from "@remix-run/node"; +import {createRequestHandler} from "@remix-run/express"; +import prometheusMiddleware from "express-prometheus-middleware"; + +const logger = log4js.getLogger(); +logger.level = "info"; + +logger.info(`Running in ${process.env.NODE_ENV === "production" ? "production" : "dev"} mode`); const viteDevServer = process.env.NODE_ENV === "production" @@ -14,8 +19,6 @@ const viteDevServer = }) ); -const logger = log4js.getLogger(); -logger.level = "info"; const app = express(); // http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header @@ -24,11 +27,12 @@ app.use(morgan("combined")); app.use( prometheusMiddleware({ collectDefaultMetrics: true, - metricsPath: `${BASE_PATH.replace(/\/$/,'')}/metrics` + metricsPath: `${BASE_PATH.replace(/\/$/, '')}/metrics` }) ); -app.use(BASE_PATH.replace(/\/$/,''), +app.use( + BASE_PATH.replace(/\/$/,''), viteDevServer ? viteDevServer.middlewares : express.static("build/client") @@ -41,7 +45,9 @@ const build = viteDevServer ) : await import("../build/server/index.js"); -app.all(`${BASE_PATH.replace(/\/$/,'')}(/*)?`, createRequestHandler({build})); +app.all( + `${BASE_PATH.replace(/\/$/,'')}(/*)?`, + createRequestHandler({build})); app.listen(PORT, () => { logger.info(`App listening on http://localhost:${PORT}${BASE_PATH.replace(/\/$/,'')}`); diff --git a/vite.config.ts b/vite.config.ts index 2f48541..e103084 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,7 +10,8 @@ installGlobals(); export default defineConfig({ base: `${BASE_PATH === "/" ? "" : BASE_PATH}/`, plugins: [remix({ - basename: `${BASE_PATH.replace(/\/$/,'')}`, + basename: `${BASE_PATH.replace(/\/$/, '')}`, + }), tsconfigPaths()], }) \ No newline at end of file