Compare commits

..

No commits in common. "main" and "2023.06.0" have entirely different histories.

37 changed files with 4364 additions and 3816 deletions

View File

@ -27,20 +27,8 @@ steps:
volumes: volumes:
- name: deps - name: deps
path: /nix/store path: /nix/store
environment:
DRONE_SSH_KEY:
from_secret: drone_ssh_key
GITEA_KNOWN_HOSTS:
from_secret: gitea_known_hosts
SSH_CONFIG:
from_secret: ssh_config
commands: commands:
- mkdir /root/.ssh - nix-build --argstr version ${DRONE_TAG} --argstr rev ${DRONE_COMMIT} --argstr repoUrl ${DRONE_REPO_LINK}
- echo "$DRONE_SSH_KEY" > /root/.ssh/id_ed25519
- chmod 400 /root/.ssh/id_ed25519
- echo "$SSH_CONFIG" > /root/.ssh/config
- echo "$GITEA_KNOWN_HOSTS" > /root/.ssh/known_hosts
- nix --extra-experimental-features nix-command --extra-experimental-features flakes build .#image
- name: publish - name: publish
image: docker:dind image: docker:dind
@ -58,7 +46,7 @@ steps:
- sleep 5 - sleep 5
- ./result | docker load - ./result | docker load
- echo $$REGISTRY_PASSWORD | docker login -u $$REGISTRY_USER --password-stdin dr.cechis.cz - echo $$REGISTRY_PASSWORD | docker login -u $$REGISTRY_USER --password-stdin dr.cechis.cz
- docker tag cv:current dr.cechis.cz/c3c/cv:${DRONE_TAG} - docker tag cv:${DRONE_TAG} dr.cechis.cz/c3c/cv:${DRONE_TAG}
- docker push dr.cechis.cz/c3c/cv:${DRONE_TAG} - docker push dr.cechis.cz/c3c/cv:${DRONE_TAG}
depends_on: depends_on:
- build - build
@ -97,20 +85,8 @@ services:
- name: dockersock - name: dockersock
path: /var/run path: /var/run
---
kind: secret
name: drone_ssh_key
data: 12kJDhu2Tb/25dBS+1HrXwn4tSuhD0S5F+z9WzIkNVl2MMbDmOlsV2XPhF1YgINsczyU7ceIWEp+tJI1X3+uMCDyuCU3zxVV4SOe3gc/2zdZ9fewz2uFxGDk6xHm/y1y9gmDfAt+//UjjjDgtGJKEKJX6r9D9diLyiF+P6m/zZCyRnTZv3SDvLv0PduOonEj/jWVVORjuBoKkshniygqqkfHL/v3TmIEvdZ0uQMCXEKKs0fEi+qOv2gpjt+tykHdlf57zSRUeDHIarKrlslGIae9HBq40CcVHLzbFMnCXpRA4wxxGbcsNnu0263fufZ169ef2Aqntgvqjxt7c0tppnI9GxMzCShBa3dHXh2mAs7irFaEAGJKtCXJ6ETMeS6/Li0Yyf1cNvW2dLVgtWGUggkW8Dbl0hD2ZqO26mu1lbxJ1v25JN0U6meNXhKFMxJDMSz+/kmPW6miMpJh3YSW7YIVZHcUrS4RoiJuYdOPD8OUcAJrHbCin3W78glqYSM1P5z0LowSlP12fNg0eYZ+5m1gML15j8DZ92PwKPKAM2WEVivfgs873cs3PVoeM8m9p3xd0qm0
---
kind: secret
name: gitea_known_hosts
data: r9Bi+OWiiOtFustCnv/oUn2ULPCHZwQKlhZ3IroztOaHsTy/RJiTsqm9Ntachs8by+skida+21YVry1oyabrbXcnClyejEuqgTKhB8fSm4Oy3GW35gZVoxRCOdYfteFgPAvX2iOpxzREwiOzhw9hcGSFNwZG5Iij+Ylp1/dD5fDc3SAczWE8M7A1mACwe2sBFv0EiG+SXOsHjH+n9P64EV52R4F7y+kSQmQ/Frjv1ZjtcbhVyP1LsLn3xhbotOI8ug0sxybRH2Mf48xC1wahdJJ54eoTDLFWBrOsopbZEVIC3wPHRSKjouSTPyzysb6QPnJdEYzQCwCNOJ4JNDrH5txVft7yLc6egyiIpiFE3IUfZHGWKeZiraqkUc3WjRIyX4+7v89HWmLlTPG08I3hBFR5aESu4NGPOp72fGjc3YKRze/nBuKDM5RXVH2AH3J9VLGD+6pfnllz9cgmHP3CEaS+8XC0rnOIpBKlNZWQJrApiWeBnLMdopy5E9ri1dPPmkvJjxaLyJ/23r35gz9RUUj/ws1AlGCIo3sM04XCXbLJbC1mgM1YDcRr6GMVztHMiMA4z0zCDzYHlQzTuSRPUl1Ygb/lyXBwVnvaajZDoyHju0D5Ie8kajcjlRxAMDcLZFlPTBJLuLp89WN9JEyKMss2L5kH0Ku7IDn3tZ7w4Ix9YJ4tN7WnjszZsks+lUbOwy5w4bKjiwwS7S9haiN2gMQ8TwCwzXIfynFS9c5xnOEzvDYTCjz04/1YbuSsYCM4HjR/ObpQWGo+1Zs5iUC9Lrhv9EaOO2q/8O7kpbqJURePN2F8f6lGRKe/aR8VGhrRrdd+YuKchL+BQaJK7q9d7m2ov3m+njNGO8/wzgUmB5GjALfYZ1nNVPta/+aMhVPrIc2iHMwlsGCx9pMClen0gtKic6MZhRWIZWxbxjoF4n9PNcBdcUW4/VvCLJRokAVHfxIH1/epH1hBrFRrDfI3hmFw8BXXIIEk4PDi1KydbJilbF3OvSOradcdzXlwIOOW2zqiq0tkeqjPrApdcCM6hkETkKgRfV9B9Ia9Ses3iy/v1bBN9Db8WjDu+KqoHfZrSkZGF2UlMjXQYLKoAPtH6ShlnaGHeeDZLk/+El6smOgeyUN6WJjKPPyBpSEZzbFfIZh7kkrqNmrkxh8ZdVlYloAuwzbCKzxCxHxDsGK5
---
kind: secret
name: ssh_config
data: 30Q8ofMygsDpqq4BcCtdv16/EWguc+mi2fYRszfZtmhe4PrIud4lxlOW5KJvyRVo7EZ2KzlZhHmGWGJKyA==
--- ---
kind: signature kind: signature
hmac: 3d73eb044fd8a4fa566e7c19e0302b6db2e888f29784353de22a3937f49bdfec hmac: 8db89c3a13466a543ec8edb72923c4985aae191b0b6de8c324bb24116a7684e9
... ...

3
.gitignore vendored
View File

@ -4,7 +4,6 @@
.envrc .envrc
.direnv .direnv
.devenv
# Server # Server
/app/server/bin/ /app/server/bin/
@ -13,11 +12,9 @@
# Frontend # Frontend
/app/frontend/node_modules /app/frontend/node_modules
/app/frontend/dist
/app/frontend/.pnp /app/frontend/.pnp
/app/frontend/.pnp.js /app/frontend/.pnp.js
/app/frontend/.dependency-graph /app/frontend/.dependency-graph
/app/frontend/eslint.shared.mjs
# testing # testing
/app/frontend/coverage /app/frontend/coverage

View File

View File

@ -0,0 +1,46 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
"eslint:recommended",
"react-app",
"airbnb-typescript",
"airbnb/hooks",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier", // Help to cooperate ESLint and Prettier
"plugin:prettier/recommended",
],
ignorePatterns: [
"/build",
"/dist",
"/node_modules",
"/.eslintrc.cjs",
"/vite.config.ts",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
project: "./tsconfig.json",
sourceType: "module",
tsconfigRootDir: __dirname,
},
plugins: ["react-refresh"],
rules: {
// Run prettier separately but use its configuration
// This speeds up on-the-fly linting during development
"prettier/prettier": "off",
"react-refresh/only-export-components": "warn",
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: true,
},
],
},
settings: {
react: {
version: "detect",
},
},
};

24
app/frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,9 @@
/node_modules
# Ignore artifacts:
/build
/dist
/coverage
# generated code
/src/api/naming
/src/preview

4
app/frontend/.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"importOrder": ["^[./]"],
"importOrderSeparation": true
}

View File

@ -1,20 +0,0 @@
import sharedConfig from "./eslint.shared.mjs";
export default [
{
ignores: [
"eslint.shared.mjs",
"prettier.config.mjs",
"vite.config.ts"
],
},
...sharedConfig,
{
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
project: "./tsconfig.app.json",
},
},
}
];

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,13 @@
"check": "npm run typecheck && echo \"Checking files with prettier\" && npm run prettier > /dev/null && npm run lint", "check": "npm run typecheck && echo \"Checking files with prettier\" && npm run prettier > /dev/null && npm run lint",
"dev": "vite", "dev": "vite",
"fix": "npm run prettier:format && npm run lint:fix", "fix": "npm run prettier:format && npm run lint:fix",
"lint": "eslint", "lint": "eslint src --ext ts,tsx",
"lint:fix": "eslint --fix", "lint:fix": "eslint --fix src --ext ts,tsx",
"lint:fix:changed": "eslint --fix $(npm run --silent list:changed)", "lint:fix:changed": "eslint --fix $(npm run --silent list:changed)",
"list:changed": "git diff HEAD --name-only --diff-filter=ACM --relative=app/console/src \"*.tsx\" \"*.ts\" \"*.js\" | sed 's#^#./src/#' ; git ls-files \"./src/*.tsx\" \"./src/*.ts\" -o --exclude-standard", "list:changed": "git diff HEAD --name-only --diff-filter=ACM --relative=app/console/src \"*.tsx\" \"*.ts\" \"*.js\" | sed 's#^#./src/#' ; git ls-files \"./src/*.tsx\" \"./src/*.ts\" -o --exclude-standard",
"prettier": "prettier --check 'src/**/*.{js,ts,tsx}'", "prettier": "prettier --config ./.prettierrc --check 'src/**/*.{js,ts,tsx}'",
"prettier:format": "prettier --write 'src/**/*.{js,ts,tsx}'", "prettier:format": "prettier --config ./.prettierrc --write 'src/**/*.{js,ts,tsx}'",
"prettier:format:changed": "prettier --write $(npm run --silent list:changed)", "prettier:format:changed": "prettier --config ./.prettierrc --write $(npm run --silent list:changed)",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
@ -25,28 +25,27 @@
"@emotion/cache": "^11.11.0", "@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.0", "@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@eslint/js": "^9.13.0", "@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/react": "^18.0.37", "@types/react": "^18.0.37",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@types/stylis": "^4.2.0", "@types/stylis": "^4.2.0",
"@vitejs/plugin-react-swc": "^3.7.1", "@typescript-eslint/eslint-plugin": "^5.59.0",
"eslint": "^9.13.0", "@typescript-eslint/parser": "^5.59.0",
"@vitejs/plugin-react-swc": "^3.0.0",
"eslint": "^8.38.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-import-resolver-typescript": "^3.6.3", "eslint-config-react-app": "^7.0.1",
"eslint-plugin-formatjs": "^5.1.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-import-x": "^4.3.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react-refresh": "^0.3.4",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"prettier": "^3.3.3",
"react-intl": "^6.4.2", "react-intl": "^6.4.2",
"react-router-dom": "^6.11.2", "react-router-dom": "^6.11.2",
"stylis": "^4.2.0", "stylis": "^4.2.0",
"typescript": "^5.6.3", "typescript": "^5.0.2",
"typescript-eslint": "^8.11.0", "vite": "^4.3.9"
"vite": "^5.4.10"
} }
} }

View File

@ -1,5 +0,0 @@
export default {
importOrder: ["^[./]"],
importOrderSeparation: true,
plugins: ["@trivago/prettier-plugin-sort-imports"],
};

View File

@ -13,7 +13,7 @@ const themeCache = createCache({
key: "cv", key: "cv",
}); });
const i = themeCache.insert.bind(themeCache); const i = themeCache.insert;
themeCache.insert = (...args) => { themeCache.insert = (...args) => {
args[2].tags.forEach((t) => { args[2].tags.forEach((t) => {
if (!t.getAttribute("media")) { if (!t.getAttribute("media")) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

View File

@ -3,92 +3,75 @@ import { defineMessages, useIntl } from "react-intl";
import { Content } from "../common/Content"; import { Content } from "../common/Content";
import { Headline, SubHeadline } from "../common/Headline"; import { Headline, SubHeadline } from "../common/Headline";
import PageBreakAvoid from "../common/PageBreakAvoid"; import { Spacer } from "../common/Spacer";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
const messages = defineMessages({ const messages = defineMessages({
skills: { skills: {
defaultMessage: "Skills", defaultMessage: "Skills",
id: "4JISx9", id: "Experience.skills",
},
yourpass: {
defaultMessage: "July 2021 - Present ({count} years)",
id: "Experience.yourpass",
},
yoursystem: {
defaultMessage: "February 2016 - December 2021 (5 years)",
id: "Experience.yoursystem",
},
past: {
defaultMessage: "Birth - February 2016",
id: "Experience.past",
},
pastDescription: {
defaultMessage:
"Working on smaller projects such as chats, forum based websites, web based presentations, most notably a system for handling the driver training center and driving school agenda... Tinkering...",
id: "Experience.pastDescription",
}, },
}); });
const approxCurrentYears = { const approxCurrentYears = Math.round(
InvestBay: Math.round( (Date.now() - new Date("2021-06-01").getTime()) / 1000 / 60 / 60 / 24 / 365
(Date.now() - new Date("2024-02-01").getTime()) / 1000 / 60 / 60 / 24 / 365, );
),
YourPass: Math.round(
(Date.now() - new Date("2021-06-01").getTime()) / 1000 / 60 / 60 / 24 / 365,
),
YourSystemSysOps: Math.round(
(Date.now() - new Date("2022-01-01").getTime()) / 1000 / 60 / 60 / 24 / 365,
),
};
const investbaySkills = [
"DevOps",
"Google Cloud Platform (GCP)",
"Amazon Web Services (AWS)",
"Gitlab",
"Terraform",
"Cloud infrastructure",
"Cloud Applications",
"Software Development Life Cycle (SDLC)",
"Linux System Administration",
"Go",
"TypeScript",
"Nix",
"GraphQL",
"gRPC",
"PostgreSQL",
"Node.js",
"React.js",
];
const yourpassSkills = [ const yourpassSkills = [
"DevOps", "DevOps",
"Software as a Service (SaaS)",
"Cloud Infrastructure",
"Kubernetes",
"Back-end Operations",
"Linux System Administration",
"Amazon Web Services (AWS)",
"Terraform",
"Go (Programming Language)",
"gRPC",
"OWASP ZAP",
"Front-End Development", "Front-End Development",
"React.js", "Back-end Operations",
"Node.js",
"TypeScript",
"Nix",
"Software Development Life Cycle (SDLC)",
"Software Development", "Software Development",
"Kubernetes",
"TypeScript",
"Go",
"Linux",
"Linux Server",
"Linux System Administration",
"Back-End Web Development",
"Git", "Git",
"PostgreSQL", "PostgreSQL",
"Node.js",
"React.js",
"Amazon Web Services (AWS)",
"Amazon EKS",
]; ];
const yoursystemSkills = [ const yoursystemSkills = [
"DevOps", "DevOps",
"Docker Swarm",
"Linux System Administration",
"Back-end Operations",
"Front-End Development", "Front-End Development",
"Amazon Web Services (AWS)", "Back-end Operations",
"Bash",
"Linux Server",
"Terraform",
"Cloud Infrastructure",
"Cloud Applications",
"Go (Programming Language)",
"TypeScript",
"Node.js",
"Software Development Life Cycle (SDLC)",
"Software Development", "Software Development",
"Kubernetes", "Kubernetes",
"TypeScript",
"Linux",
"Linux Server",
"Linux System Administration",
"Back-End Web Development", "Back-End Web Development",
"GraphQL",
"Git", "Git",
"Node.js",
"Vue.js",
"PHP",
"Bash",
"Amazon Web Services (AWS)",
"Amazon EKS",
]; ];
const Experience: React.FC = () => { const Experience: React.FC = () => {
@ -96,109 +79,40 @@ const Experience: React.FC = () => {
return ( return (
<> <>
<PageBreakAvoid>
<Headline level={3}>INVESTBAY s.r.o.</Headline>
<SubHeadline level={4}>DevOps Architect</SubHeadline>
<Content>
<Paragraph>
{intl.formatMessage(
{
defaultMessage: "February 2024 - Present",
id: "w2Pn7j",
},
{
// not used atm
count: approxCurrentYears.InvestBay,
},
)}
</Paragraph>
<Paragraph>
<strong>{intl.formatMessage(messages.skills)}:</strong>{" "}
{investbaySkills.join(" · ")}
</Paragraph>
</Content>
</PageBreakAvoid>
<PageBreakAvoid>
<Headline level={3}>YOUR PASS s.r.o.</Headline> <Headline level={3}>YOUR PASS s.r.o.</Headline>
<SubHeadline level={4}>DevOps Engineer</SubHeadline> <SubHeadline level={4}>DevOps Engineer</SubHeadline>
<Content> <Content>
<Paragraph> <Paragraph>
{intl.formatMessage( {intl.formatMessage(messages.yourpass, {
{ count: approxCurrentYears,
defaultMessage: "July 2021 - Present ({count} years)", })}
id: "6T3Cls",
},
{
count: approxCurrentYears.YourPass,
},
)}
</Paragraph> </Paragraph>
<Paragraph> <Paragraph>
<strong>{intl.formatMessage(messages.skills)}:</strong>{" "} <strong>{intl.formatMessage(messages.skills)}:</strong>{" "}
{yourpassSkills.join(" · ")} {yourpassSkills.join(" · ")}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
<PageBreakAvoid> <Spacer />
<Headline level={3}>YOUR SYSTEM s.r.o.</Headline> <Headline level={3}>YOUR SYSTEM s.r.o.</Headline>
<SubHeadline level={4}>SysOps</SubHeadline>
<Content>
<Paragraph>
{intl.formatMessage(
{
defaultMessage: "January 2022 - Present ({count} years)",
id: "nqa9U0",
},
{
count: approxCurrentYears.YourSystemSysOps,
},
)}
</Paragraph>
<Paragraph>
<strong>{intl.formatMessage(messages.skills)}:</strong>{" "}
{yoursystemSkills.join(" · ")}
</Paragraph>
</Content>
<SubHeadline level={4}>Lead Developer/Architect</SubHeadline> <SubHeadline level={4}>Lead Developer/Architect</SubHeadline>
<Content> <Content>
<Paragraph> <Paragraph>{intl.formatMessage(messages.yoursystem)}</Paragraph>
{intl.formatMessage({
defaultMessage: "February 2016 - December 2021 (5 years)",
id: "96SwL8",
})}
</Paragraph>
<Paragraph> <Paragraph>
<strong>{intl.formatMessage(messages.skills)}:</strong>{" "} <strong>{intl.formatMessage(messages.skills)}:</strong>{" "}
{yoursystemSkills.join(" · ")} {yoursystemSkills.join(" · ")}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
<PageBreakAvoid>
<Headline level={3}>Past experience</Headline> <Headline level={3}>Past experience</Headline>
<SubHeadline level={4}>Developer</SubHeadline> <SubHeadline level={4}>Developer</SubHeadline>
<Content> <Content>
<Paragraph> <Paragraph>{intl.formatMessage(messages.past)}</Paragraph>
{intl.formatMessage({ <Paragraph>{intl.formatMessage(messages.pastDescription)}</Paragraph>
defaultMessage: "Birth - February 2016",
id: "KtILZ7",
})}
</Paragraph>
<Paragraph>
{intl.formatMessage({
defaultMessage:
"Working on smaller projects such as chats, forum based websites, web based presentations, most notably a system for handling the driver training center and driving school agenda... Tinkering...",
id: "obpLg6",
})}
</Paragraph>
</Content> </Content>
</PageBreakAvoid>
</> </>
); );
}; };

View File

@ -1,6 +1,5 @@
import React, { Children } from "react"; import React from "react";
import PageBreakAvoid from "../common/PageBreakAvoid";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
@ -12,13 +11,13 @@ const Skill: React.FC<React.PropsWithChildren<SkillProps>> = (props) => {
const { children, title } = props; const { children, title } = props;
return ( return (
<PageBreakAvoid> <div>
<strong>{title}</strong> <strong>{title}</strong>
<Spacer /> <Spacer />
{Children.map(children, (c) => ( {React.Children.map(children, (c) => (
<Paragraph>{c}</Paragraph> <Paragraph>{c}</Paragraph>
))} ))}
</PageBreakAvoid> </div>
); );
}; };

View File

@ -1,13 +1,49 @@
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { SubHeadline } from "../common/Headline"; import { SubHeadline } from "../common/Headline";
import { List, ListItem } from "../common/List"; import { List, ListItem } from "../common/List";
import PageBreakAvoid from "../common/PageBreakAvoid";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
import Skill from "./Skill"; import Skill from "./Skill";
const messages = defineMessages({
backendDevelopment: {
defaultMessage:
"Deep knowledge of backend operations, mostly supporting web based applications using various technologies, programming languages and frameworks.",
id: "Skills.backendDevelopment",
},
backendDevelopmentAdditional: {
defaultMessage:
"In recent years, the main focus was on Go to write tooling and backend services.",
id: "Skills.backendDevelopment",
},
devops: {
defaultMessage:
"Several years of experience developing, maintaining and adhering to the DevOps practices to streamline organization's processes across both backend and frontend development.",
id: "Skills.devops",
},
frontendDevelopment: {
defaultMessage:
"Extensive experience in frontend development spanning back to the early 2000s.",
id: "Skills.frontendDevelopment",
},
infrastructureOperations: {
defaultMessage:
"Throughout my career, I have gained valuable experience in developing and maintaining high-load infrastructures over several years.",
id: "Skills.infrastructureOperations",
},
infrastructureOperationsAdditional: {
defaultMessage:
"Following the OCI (Open Container Initiative) and leveraging Docker and Kubernetes to create highly available deployments, primarily on the AWS platform, was a challenging and enjoyable experience, that has greatly enhanced my skills in this area.",
id: "Skills.infrastructureOperationsAdditional",
},
otherNotableSkills: {
defaultMessage: "Other notable skills",
id: "Skills.otherNotableSkills",
},
});
type OtherSkill = { type OtherSkill = {
title: string; title: string;
description?: string; description?: string;
@ -15,13 +51,13 @@ type OtherSkill = {
const otherSkills: OtherSkill[] = [ const otherSkills: OtherSkill[] = [
{ {
title: "Strong proficiency in", title: "Primary languages",
description: "IaaC tooling, Go, Typescript (and Javascript), Bash", description: "Go, Typescript (and Javascript)",
}, },
{ {
title: "Infrastructure related skills", title: "Infrastructure based skills",
description: description:
"Kubernetes and AWS EKS, Containerized applications, Linux system administration, Amazon Web Services - good understanding of Networking, Databases, Queues and many other managed services. Notable experience with GCP/Azure. Experience with most major platforms such as Github, Gitlab, Gitea, Bitbucket and their tooling.", "Kubernetes and AWS EKS, Containerized applications, Linux system administration, Amazon Web Services - good understanding of Networking, RDS, Queues",
}, },
{ {
title: "Databases", title: "Databases",
@ -30,7 +66,7 @@ const otherSkills: OtherSkill[] = [
{ {
title: "Some of the relevant Frameworks/Tooling", title: "Some of the relevant Frameworks/Tooling",
description: description:
"Terraform, Prometheus, Grafana, InfluxDB, Prometheus, Elasticsearch, Nix, OWASP ZAP, React, Esbuild, deep understanding of Git", "Terraform, Prometheus, Grafana, React, Webpack, Rollup, Esbuild, deep understanding of Git",
}, },
{ {
title: "Software Development", title: "Software Development",
@ -52,58 +88,27 @@ const Skills: React.FC = () => {
return ( return (
<> <>
<Skill title="DevOps"> <Skill title="DevOps">{intl.formatMessage(messages.devops)}</Skill>
{intl.formatMessage({ <Skill title="Front-End Development">
defaultMessage: {intl.formatMessage(messages.frontendDevelopment)}
"Several years of experience developing, maintaining and adhering to the DevOps practices to streamline organization's processes across both backend and frontend development.",
id: "BOZ+do",
})}
</Skill>
<Skill title="Infrastructure operations">
{intl.formatMessage({
defaultMessage:
"Throughout my career, I have gained valuable experience in developing, maintaining and monitoring high-load infrastructure clusters.",
id: "PKLG9N",
})}
{intl.formatMessage({
defaultMessage:
"Following the OCI (Open Container Initiative) and leveraging Docker and Kubernetes to create highly available deployments, primarily on the AWS platform, was a challenging and enjoyable experience, that has greatly enhanced my skills in this area.",
id: "VRhCte",
})}
</Skill> </Skill>
<Skill title="Back-End Development"> <Skill title="Back-End Development">
{intl.formatMessage({ {intl.formatMessage(messages.backendDevelopment)}
defaultMessage: {intl.formatMessage(messages.backendDevelopmentAdditional)}
"Deep knowledge of backend operations, mostly supporting web based applications using various technologies, programming languages and frameworks.",
id: "h4cHqQ",
})}
{intl.formatMessage({
defaultMessage:
"In recent years, my focus was on Go to write tooling and backend services.",
id: "y/ymnB",
})}
</Skill> </Skill>
<Skill title="Front-End Development"> <Skill title="Infrastructure operations">
{intl.formatMessage({ {intl.formatMessage(messages.infrastructureOperations)}
defaultMessage: {intl.formatMessage(messages.infrastructureOperationsAdditional)}
"Extensive experience in frontend development spanning back to the early 2000s.",
id: "AjT8sf",
})}
</Skill> </Skill>
<Spacer withDivider /> <Spacer withDivider />
<SubHeadline level={4}> <SubHeadline level={4}>
{intl.formatMessage({ {intl.formatMessage(messages.otherNotableSkills)}
defaultMessage: "Other notable skills",
id: "8NqNwY",
})}
</SubHeadline> </SubHeadline>
<List> <List>
{otherSkills.map((s) => ( {otherSkills.map((s) => (
<ListItem key={s.title}> <ListItem key={s.title}>
<PageBreakAvoid>
<strong>{s.title}</strong> <strong>{s.title}</strong>
{s.description && `: ${s.description}`} {s.description && `: ${s.description}`}
</PageBreakAvoid>
</ListItem> </ListItem>
))} ))}
</List> </List>

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { Paragraph, Caption } from "../common/Text"; import { Paragraph } from "../common/Text";
import { Caption } from "../common/Text";
const Summary: React.FC = () => { const Summary: React.FC = () => {
return ( return (
@ -37,7 +38,8 @@ const Summary: React.FC = () => {
knowledge of KNX systems. knowledge of KNX systems.
</Paragraph> </Paragraph>
<Caption> <Caption>
-- Summary graciously generated by an AI and curated by human -- This summary was graciously generated by an AI and then rewritten and
adjusted by a human
</Caption> </Caption>
</> </>
); );

View File

@ -1,22 +1,22 @@
import React, { Component } from "react"; import React from "react";
type ErrorHandler = (error: Error, info: React.ErrorInfo) => void; type ErrorHandler = (error: Error, info: React.ErrorInfo) => void;
type ErrorHandlingComponent<Props> = ( type ErrorHandlingComponent<Props> = (
props: Props, props: Props,
error?: Error, error?: Error
) => React.ReactNode; ) => React.ReactNode;
type ErrorState = { error?: Error }; type ErrorState = { error?: Error };
function Catch<Props extends object>( function Catch<Props extends object>(
component: ErrorHandlingComponent<Props>, component: ErrorHandlingComponent<Props>,
errorHandler?: ErrorHandler, errorHandler?: ErrorHandler
): React.ComponentType<Props> { ): React.ComponentType<Props> {
function Inner({ error, props }: { error?: Error; props: Props }) { function Inner({ error, props }: { error?: Error; props: Props }) {
return <React.Fragment>{component(props, error)}</React.Fragment>; return <React.Fragment>{component(props, error)}</React.Fragment>;
} }
return class CatchClass extends Component<Props, ErrorState> { return class CatchClass extends React.Component<Props, ErrorState> {
state: ErrorState = { state: ErrorState = {
error: undefined, error: undefined,
}; };
@ -46,7 +46,7 @@ const divStyles: React.CSSProperties = {
const ErrorBoundary = Catch(function ( const ErrorBoundary = Catch(function (
props: React.PropsWithChildren, props: React.PropsWithChildren,
error?: Error, error?: Error
) { ) {
if (error) { if (error) {
return ( return (

View File

@ -6,10 +6,6 @@ import { hideNavigationMQ } from "./navigationBreakpoint";
const Root = styled("div")({ const Root = styled("div")({
background: "rgb(215, 215, 215)", background: "rgb(215, 215, 215)",
"@media print": {
// Nav background
background: "rgb(41, 62, 73)",
},
}); });
const Container = styled("div")({ const Container = styled("div")({
@ -27,7 +23,7 @@ const Nav = styled("nav")({
flex: "0 0 280px", flex: "0 0 280px",
width: 280, width: 280,
minHeight: "100%", minHeight: "100%",
padding: "0 2rem 2rem", padding: "3rem 2rem 2rem",
[hideNavigationMQ]: { [hideNavigationMQ]: {
display: "none", display: "none",

View File

@ -1,140 +1,70 @@
import styled from "@emotion/styled"; import React from "react";
import React, { useEffect, useRef } from "react"; import { defineMessages, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import profile from "../../assets/profile.jpg";
import { CONTACT_EMAIL, CONTACT_PHONE } from "../../config/environment"; import { CONTACT_EMAIL, CONTACT_PHONE } from "../../config/environment";
import { Contact } from "../common/Contact"; import { Contact } from "../common/Contact";
import { List, ListItem } from "../common/List"; import { List, ListItem } from "../common/List";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import NavigationHeadline from "./NavigationHeadline"; import NavigationHeadline from "./NavigationHeadline";
const ProfileImg = styled("img")({ const messages = defineMessages({
borderRadius: "50%", certifications: {
border: "2px solid white", defaultMessage: "Certifications",
display: "block", id: "Navigation.certifications",
margin: "0 auto", },
maxWidth: "100%", contact: {
width: 150, defaultMessage: "Contact",
}); id: "Navigation.contact",
},
const navBottomPadding = 50; experience: {
defaultMessage: "Experience",
const TopSpacer = styled("div")({ id: "Navigation.experience",
height: 40, },
}); objective: {
defaultMessage: "Objective",
const StickyWrapper = styled("div")({ id: "Navigation.objective",
position: "sticky", },
topSkills: {
defaultMessage: "Top Skills",
id: "Navigation.topSkills",
},
}); });
const MainNavigation: React.FC = () => { const MainNavigation: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const d = ref.current;
if (!d) {
return;
}
d.style.top = "0px";
let { height } = d.getBoundingClientRect();
const listener = () => {
const visibleArea = navBottomPadding + height;
if (window.innerHeight > visibleArea) {
d.style.top = "0px";
} else {
d.style.top = `${window.innerHeight - visibleArea}px`;
}
};
const ro = new ResizeObserver((entries) => {
if (entries[0]) {
height = entries[0].contentRect.height;
listener();
}
});
ro.observe(d);
window.addEventListener("scroll", listener, { passive: true });
window.addEventListener("resize", listener, { passive: true });
return () => {
window.removeEventListener("scroll", listener);
ro.disconnect();
};
}, []);
return ( return (
<StickyWrapper ref={ref}> <>
<TopSpacer />
<ProfileImg src={profile} alt="profile picture" />
<NavigationHeadline> <NavigationHeadline>
{intl.formatMessage({ {intl.formatMessage(messages.contact)}
defaultMessage: "Contact",
id: "s3RWxF",
})}
</NavigationHeadline> </NavigationHeadline>
<Spacer /> <Spacer />
<Contact email={CONTACT_EMAIL} phoneNumber={CONTACT_PHONE} /> <Contact email={CONTACT_EMAIL} phoneNumber={CONTACT_PHONE} />
<NavigationHeadline href="#objective"> <NavigationHeadline to="#objective">
{intl.formatMessage({ {intl.formatMessage(messages.objective)}
defaultMessage: "Objective",
id: "UMiSC4",
})}
</NavigationHeadline> </NavigationHeadline>
<Spacer /> <NavigationHeadline to="#skills">
{intl.formatMessage(messages.topSkills)}
<List>
<ListItem>
{intl.formatMessage(
{
defaultMessage: "If there is a need,{br}there is a path.",
id: "Wg8h9t",
},
{ br: <br /> },
)}
</ListItem>
</List>
<NavigationHeadline href="#skills">
{intl.formatMessage({
defaultMessage: "Top Skills",
id: "JUWjX4",
})}
</NavigationHeadline> </NavigationHeadline>
<Spacer /> <Spacer />
<List> <List>
<ListItem>DevOps</ListItem> <ListItem>DevOps</ListItem>
<ListItem>Kubernetes</ListItem>
<ListItem>Linux</ListItem>
<ListItem>Go</ListItem>
<ListItem>Typescript</ListItem>
<ListItem>Back-End Development</ListItem>
<ListItem>Front-End Development</ListItem> <ListItem>Front-End Development</ListItem>
<ListItem>Scripting:</ListItem> <ListItem>Back-End Development</ListItem>
<ListItem>Bash, Python, Lua</ListItem> <ListItem>Kubernetes</ListItem>
<ListItem>Typescript</ListItem>
<ListItem>Go</ListItem>
</List> </List>
<Spacer spacing={0} withDivider /> <Spacer spacing={0} withDivider />
<NavigationHeadline> <NavigationHeadline to="#certifications">
{intl.formatMessage({ {intl.formatMessage(messages.certifications)}
defaultMessage: "Certifications",
id: "tl429z",
})}
</NavigationHeadline> </NavigationHeadline>
<Spacer /> <Spacer />
@ -146,13 +76,10 @@ const MainNavigation: React.FC = () => {
<Spacer spacing={0} withDivider /> <Spacer spacing={0} withDivider />
<NavigationHeadline href="#experience"> <NavigationHeadline to="#experience">
{intl.formatMessage({ {intl.formatMessage(messages.experience)}
defaultMessage: "Experience",
id: "W1qv5r",
})}
</NavigationHeadline> </NavigationHeadline>
</StickyWrapper> </>
); );
}; };

View File

@ -15,25 +15,17 @@ const Link = styled(RouterLink)({
textDecoration: "none", textDecoration: "none",
}); });
type NavigationHeadlineProps = type NavigationHeadlineProps = Partial<Pick<RouterLinkProps, "to">>;
| (Partial<Pick<RouterLinkProps, "to">> & {
href?: undefined;
})
| {
href?: string;
to?: undefined;
};
const NavigationHeadline: React.FC< const NavigationHeadline: React.FC<
React.PropsWithChildren<NavigationHeadlineProps> React.PropsWithChildren<NavigationHeadlineProps>
> = (props) => { > = (props) => {
const { children, href, to } = props; const { children, to } = props;
return ( return (
<Headline> <Headline>
{to && <Link to={to}>{children}</Link>} {to && <Link to={to}>{children}</Link>}
{href && <a href={href}>{children}</a>} {!to && children}
{!to && !href && children}
</Headline> </Headline>
); );
}; };

View File

@ -1,7 +0,0 @@
import styled from "@emotion/styled";
const PageBreakAvoid = styled("div")({
pageBreakInside: "avoid",
});
export default PageBreakAvoid;

View File

@ -25,18 +25,18 @@ const Space = styled("div", { shouldForwardProp })<SpaceProps>(
({ spacing, theme }) => ({ ({ spacing, theme }) => ({
width: "100%", width: "100%",
height: theme.spacing(spacing === undefined ? 1 : spacing), height: theme.spacing(spacing === undefined ? 1 : spacing),
}), })
); );
const Spacer: React.FC<SpacerProps> = (props) => { const Spacer: React.FC<SpacerProps> = (props) => {
const { spacing, withDivider } = props; const { spacing, withDivider } = props;
if (!withDivider) { if (!withDivider) {
const spaceSize = spacing === undefined ? 2 : spacing; const spaceSize = typeof spacing === undefined ? 2 : spacing;
return <Space spacing={spaceSize} />; return <Space spacing={spaceSize} />;
} }
const spaceSize = spacing === undefined ? 1 : spacing; const spaceSize = typeof spacing === undefined ? 1 : spacing;
return ( return (
<Root> <Root>

View File

@ -1,6 +1,6 @@
import styled from "@emotion/styled"; import styled from "@emotion/styled";
const Caption = styled("div")(({ theme }) => ({ const Caption = styled("caption")(({ theme }) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
fontSize: "0.75rem", fontSize: "0.75rem",
lineHeight: 1.5, lineHeight: 1.5,

View File

@ -28,21 +28,15 @@ export const BUILD_TIMESTAMP: Date = window.APP_CONFIG?.commitTime
export const CONTACT_EMAIL = confString( export const CONTACT_EMAIL = confString(
window.APP_CONFIG?.contactEmail, window.APP_CONFIG?.contactEmail,
typeof import.meta.env.VITE_CONTACT_EMAIL === "string" import.meta.env.VITE_CONTACT_EMAIL || ""
? import.meta.env.VITE_CONTACT_EMAIL
: "",
); );
export const CONTACT_PHONE = confString( export const CONTACT_PHONE = confString(
window.APP_CONFIG?.contactPhone, window.APP_CONFIG?.contactPhone,
typeof import.meta.env.VITE_CONTACT_PHONE === "string" import.meta.env.VITE_CONTACT_PHONE || ""
? import.meta.env.VITE_CONTACT_PHONE
: "",
); );
export const POSITION = confString( export const POSITION = confString(
window.APP_CONFIG?.position, window.APP_CONFIG?.position,
typeof import.meta.env.VITE_POSITION === "string" import.meta.env.VITE_POSITION || ""
? import.meta.env.VITE_POSITION
: "",
); );

View File

@ -1,10 +1,10 @@
import React from "react"; import React from "react";
import { createRoot } from "react-dom/client"; import ReactDOM from "react-dom/client";
import App from "./App"; import App from "./App";
createRoot(document.getElementById("root") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>
); );

View File

@ -1,6 +1,6 @@
import styled from "@emotion/styled"; import styled from "@emotion/styled";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { Experience, Skills, Summary } from "../components/CV"; import { Experience, Skills, Summary } from "../components/CV";
import MainLayout from "../components/Layout/MainLayout"; import MainLayout from "../components/Layout/MainLayout";
@ -11,6 +11,21 @@ import { Headline, SubHeadline } from "../components/common/Headline";
import { Spacer } from "../components/common/Spacer"; import { Spacer } from "../components/common/Spacer";
import { CONTACT_EMAIL, CONTACT_PHONE, POSITION } from "../config/environment"; import { CONTACT_EMAIL, CONTACT_PHONE, POSITION } from "../config/environment";
const messages = defineMessages({
experienceTitle: {
defaultMessage: "Experience",
id: "Dashboard.experienceTitle",
},
location: {
defaultMessage: "Central Bohemia, Czechia",
id: "Dashboard.location",
},
skillsTitle: {
defaultMessage: "Skills",
id: "Dashboard.skillsTitle",
},
});
const HiddenWithNav = styled("div")({ const HiddenWithNav = styled("div")({
display: "none", display: "none",
[hideNavigationMQ]: { [hideNavigationMQ]: {
@ -26,10 +41,7 @@ const Dashboard: React.FC = () => {
<Headline level={1}>Lukáš Čech</Headline> <Headline level={1}>Lukáš Čech</Headline>
<SubHeadline level={2}>{POSITION}</SubHeadline> <SubHeadline level={2}>{POSITION}</SubHeadline>
<SubHeadline level={4}> <SubHeadline level={4}>
{intl.formatMessage({ {intl.formatMessage(messages.location)}
defaultMessage: "Central Bohemia, Czechia",
id: "Lkk994",
})}
</SubHeadline> </SubHeadline>
<HiddenWithNav> <HiddenWithNav>
@ -43,30 +55,20 @@ const Dashboard: React.FC = () => {
<Summary /> <Summary />
</Content> </Content>
<div id="experience" />
<Spacer withDivider /> <Spacer withDivider />
<div id="skills" /> <Headline level={2}>{intl.formatMessage(messages.skillsTitle)}</Headline>
<Headline level={2}>
{intl.formatMessage({
defaultMessage: "Skills",
id: "4JISx9",
})}
</Headline>
<Spacer /> <Spacer />
<Content> <Content>
<Skills /> <Skills />
</Content> </Content>
<div id="experience" />
<Spacer withDivider /> <Spacer withDivider />
<div id="experience" />
<Headline level={2}> <Headline level={2}>
{intl.formatMessage({ {intl.formatMessage(messages.experienceTitle)}
defaultMessage: "Experience",
id: "W1qv5r",
})}
</Headline> </Headline>
<Spacer /> <Spacer />
<Content> <Content>

View File

@ -1,9 +1,16 @@
import styled from "@emotion/styled"; import styled from "@emotion/styled";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import { Link } from "../components/common/Link"; import { Link } from "../components/common/Link";
const messages = defineMessages({
notFound: {
defaultMessage: "Nothing to see here",
id: "Router.NotFound",
},
});
const Root = styled("div")({ const Root = styled("div")({
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
@ -18,12 +25,7 @@ const NotFound = () => {
return ( return (
<Root> <Root>
<p> <p>{intl.formatMessage(messages.notFound)}</p>
{intl.formatMessage({
defaultMessage: "Nothing to see here",
id: "ywIjM0",
})}
</p>
<Link to="/">Go home</Link> <Link to="/">Go home</Link>
</Root> </Root>
); );

View File

@ -1,5 +1,6 @@
import React, { lazy } from "react"; import React from "react";
import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom"; import { Outlet, RouterProvider } from "react-router-dom";
import { createBrowserRouter } from "react-router-dom";
import { ErrorBoundary } from "../components/ErrorBoundary"; import { ErrorBoundary } from "../components/ErrorBoundary";
import Dashboard from "./Dashboard"; import Dashboard from "./Dashboard";
@ -12,7 +13,7 @@ const Layout: React.FC = () => {
); );
}; };
const NotFound = lazy(() => import("./NotFound")); const NotFound = React.lazy(() => import("./NotFound"));
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {

View File

@ -1,28 +0,0 @@
{
"compilerOptions": {
"composite": true,
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@ -1,7 +1,23 @@
{ {
"files": [], "compilerOptions": {
"references": [ "target": "ES2020",
{ "path": "./tsconfig.app.json" }, "useDefineForClassFields": true,
{ "path": "./tsconfig.node.json" } "lib": ["ES2020", "DOM", "DOM.Iterable"],
] "module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
} }

View File

@ -1,25 +0,0 @@
{
"compilerOptions": {
"composite": true,
"allowSyntheticDefaultImports": true,
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -67,6 +67,7 @@ func (h *handler) serveFile(w http.ResponseWriter, r *http.Request, modtime time
gw.Reset(io.Discard) gw.Reset(io.Discard)
} }
err = gw.Close() err = gw.Close()
if err != nil { if err != nil {
h.logger.Warn("Could not close gzip writer", zap.Error(err)) h.logger.Warn("Could not close gzip writer", zap.Error(err))
return return

View File

@ -33,6 +33,7 @@ func main() {
var err error var err error
logger, err = config.LoggerConfig.Build() logger, err = config.LoggerConfig.Build()
if err != nil { if err != nil {
fmt.Println(notice) fmt.Println(notice)
fmt.Println(version.CommitTime.Format("Committed at 2006/01/02 15:04:05 MST")) fmt.Println(version.CommitTime.Format("Committed at 2006/01/02 15:04:05 MST"))

128
default.nix Normal file
View File

@ -0,0 +1,128 @@
{ system ? builtins.currentSystem
, version ? "latest"
, rev ? ""
, commitTime ? "1970-01-01T00:00:00+00:00"
, repoUrl ? ""
}:
let
nixpkgs = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/4ecab3273592f27479a583fb6d975d4aba3486fe.tar.gz";
sha256 = "10wn0l08j9lgqcw8177nh2ljrnxdrpri7bp0g7nvrsn9rkawvlbf";
};
pkgs = import nixpkgs { inherit system; };
devshell = import
(pkgs.fetchFromGitHub {
owner = "numtide";
repo = "devshell";
rev = "5143ea68647c4cf5227e4ad2100db6671fc4c369";
sha256 = "sha256-9goJTd05yOyD/McaMqZ4BUB8JW+mZMnZQJZ7VQ6C/Lw=";
})
{ nixpkgs = pkgs; };
nodejs = pkgs.nodejs_20;
go = pkgs.go_1_20;
dream2nix = pkgs.dream2nix;
buildGoModule = pkgs.buildGoModule.override { inherit go; };
golangci-lint = pkgs.golangci-lint.override { inherit buildGoModule; };
src = pkgs.lib.cleanSource ./.;
uiSrc = pkgs.lib.cleanSource ./app/frontend;
package = pkgs.buildNpmPackage {
name = "ui";
buildInputs = [
nodejs
];
pname = "ui";
src = uiSrc;
buildPhase = ''
npm run check
npm run build
mkdir $out
cp -a dist $out
'';
npmInstallFlags = "--no-audit --no-progress --no-fund";
npmDepsHash = "sha256-dnUbw0kfAA50ZaQ91q/fC+gZhAGNo6vrSQBz6Qf2ngo=";
npmPackFlags = [ "--ignore-scripts" ];
};
server = buildGoModule {
inherit src version;
pname = "cv";
postPatch = ''
rm -rf app/server/internal/files/data/public
mkdir -p app/server/internal/files/data/public
touch app/server/internal/files/data/public/.gitkeep
cp -Tr ${package}/dist/ app/server/internal/files/data/public/
chmod +w -R app/server/internal/files/data/public
sed -i 's#<script#<script nonce="{{ .CspNonce }}"#g' app/server/internal/files/data/public/index.html
'';
CGO_ENABLED = 0;
ldflags = "-s -w -X gopkg.c3c.cz/cv/app/server/internal/version.Tag=${version} -X gopkg.c3c.cz/cv/app/server/internal/version.Commit=${rev} -X gopkg.c3c.cz/cv/app/server/internal/version.commitTime=${commitTime} -X gopkg.c3c.cz/cv/app/server/internal/version.repoUrl=${repoUrl}";
subPackages = [ "app/server" ];
vendorSha256 = "sha256-BkwXD8n62+p639vTLwXpCiY8cqYtCjWxaa5tuw78u5g=";
overrideModAttrs = (_: {
impureEnvVars = pkgs.lib.fetchers.proxyImpureEnvVars ++ [ "GIT_PROXY_COMMAND" "SOCKS_SERVER" "GOPROXY" ];
});
};
in
{
image = pkgs.dockerTools.streamLayeredImage {
name = "cv";
tag = version;
config = {
Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
Entrypoint = [ "${server}/bin/server" ];
};
};
shell = devshell.mkShell {
devshell = {
name = "c3c-cv";
packages = [
go
golangci-lint
nodejs
# keep pinned nixpkgs so they are not garbage collected
pkgs.path
];
};
commands = [
{
name = "lint";
help = "run golangci-lint and npm run check";
command = ''
cd ''$PRJ_ROOT
golangci-lint run --sort-results --out-format tab
npm --prefix app/frontend run check
'';
}
{
name = "fmt";
help = "Format and fix found issues";
command = ''
cd ''$PRJ_ROOT
golangci-lint run --sort-results --out-format tab --fix
npm --prefix app/frontend run fix
'';
}
{
name = "dev";
help = "Starts the javascript project in dev";
command = ''
npm --prefix ''$PRJ_ROOT/app/frontend run dev
'';
}
];
};
}

View File

@ -1,686 +0,0 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": "devenv_2",
"flake-compat": [
"nix",
"devenv",
"flake-compat"
],
"git-hooks": [
"nix",
"devenv",
"pre-commit-hooks"
],
"nixpkgs": [
"nix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1726520618,
"narHash": "sha256-jOsaBmJ/EtX5t/vbylCdS7pWYcKGmWOKg4QKUzKr6dA=",
"owner": "cachix",
"repo": "cachix",
"rev": "695525f9086542dfb09fde0871dbf4174abbf634",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "cachix",
"type": "github"
}
},
"cachix_2": {
"inputs": {
"devenv": "devenv_3",
"flake-compat": [
"nix",
"devenv",
"cachix",
"devenv",
"flake-compat"
],
"nixpkgs": [
"nix",
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"pre-commit-hooks": [
"nix",
"devenv",
"cachix",
"devenv",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1712055811,
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
"owner": "cachix",
"repo": "cachix",
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat_2",
"nix": "nix_4",
"nixpkgs": [
"nix",
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks_2"
},
"locked": {
"lastModified": 1729857292,
"narHash": "sha256-+uYhri8aC9Jdu6fSfjkFOmBIYVWYQnF6mdFj+ophud0=",
"owner": "cachix",
"repo": "devenv",
"rev": "d9156ba99fed6f4bc4d84b8ff1b442f04a98210a",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_2": {
"inputs": {
"cachix": "cachix_2",
"flake-compat": [
"nix",
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix_3",
"nixpkgs": [
"nix",
"devenv",
"cachix",
"nixpkgs"
],
"pre-commit-hooks": [
"nix",
"devenv",
"cachix",
"git-hooks"
]
},
"locked": {
"lastModified": 1723156315,
"narHash": "sha256-0JrfahRMJ37Rf1i0iOOn+8Z4CLvbcGNwa2ChOAVrp/8=",
"owner": "cachix",
"repo": "devenv",
"rev": "ff5eb4f2accbcda963af67f1a1159e3f6c7f5f91",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_3": {
"inputs": {
"flake-compat": [
"nix",
"devenv",
"cachix",
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix_2",
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix",
"pre-commit-hooks": [
"nix",
"devenv",
"cachix",
"devenv",
"cachix",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1708704632,
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
"owner": "cachix",
"repo": "devenv",
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nix",
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"nix",
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1729891574,
"narHash": "sha256-OOAwctBTUwKc9yTvvK1+sbcGcYqHSYvI060DlczMUbI=",
"ref": "refs/heads/main",
"rev": "986890b04467a51533b03a5d5c4cf4ea5ebc4cbb",
"revCount": 7,
"type": "git",
"url": "ssh://git@git.c3c.cz/C3C/nix"
},
"original": {
"type": "git",
"url": "ssh://git@git.c3c.cz/C3C/nix"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"nix",
"devenv",
"cachix",
"devenv",
"cachix",
"devenv",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix_2": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nix",
"devenv",
"cachix",
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix_3": {
"inputs": {
"flake-compat": [
"nix",
"devenv",
"cachix",
"devenv",
"flake-compat"
],
"nixpkgs": [
"nix",
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression_2"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix_4": {
"inputs": {
"flake-compat": [
"nix",
"devenv",
"flake-compat"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": "nixpkgs-23-11",
"nixpkgs-regression": "nixpkgs-regression_3",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1727438425,
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1692808169,
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-23-11": {
"locked": {
"lastModified": 1717159533,
"narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_2": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_3": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1720386169,
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1728538411,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"nix",
"devenv",
"cachix",
"devenv",
"cachix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1692876271,
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"nix",
"devenv",
"nix"
],
"flake-utils": "flake-utils_2",
"gitignore": [
"nix",
"devenv",
"nix"
],
"nixpkgs": [
"nix",
"devenv",
"nix",
"nixpkgs"
],
"nixpkgs-stable": [
"nix",
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712897695,
"narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"pre-commit-hooks_2": {
"inputs": {
"flake-compat": [
"nix",
"devenv",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"nix",
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1726745158,
"narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"nix": "nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

151
flake.nix
View File

@ -1,151 +0,0 @@
{
inputs = {
nix.url = "git+ssh://git@git.c3c.cz/C3C/nix";
};
outputs =
{ self, nix }:
{
formatter = nix.formatter;
packages = nix.lib.forAllSystems (
pkgs:
let
version = "rev-${self.shortRev or self.dirtyShortRev}";
package = builtins.fromJSON (builtins.readFile ./app/frontend/package.json);
in
rec {
devenv-up = self.devShells.${pkgs.system}.default.config.procfileScript;
devenv-test = self.devShells.${pkgs.system}.default.config.test;
ui = pkgs.buildNpmPackage {
inherit version;
pname = "cv";
src = ./app/frontend;
preBuild = ''
cp ${nix.lib.eslint-config-file} ./eslint.shared.mjs
npm run check
'';
npmInstallFlags = "--no-audit --no-progress --no-fund";
npmDepsHash = "sha256-P+TNtlilrzlnkclZtYrG163ucEavm9koei69ZG3sOSM=";
npmPackFlags = [ "--ignore-scripts" ];
};
server = pkgs.buildGoModule {
inherit version;
pname = "cv";
CGO_ENABLED = 0;
src = ./.;
subPackages = [ "app/server" ];
postPatch = ''
rm -rf app/server/internal/files/data/public
mkdir -p app/server/internal/files/data/public
touch app/server/internal/files/data/public/.gitkeep
cp -Tr ${ui}/lib/node_modules/${package.name}/dist app/server/internal/files/data/public/
chmod +w -R app/server/internal/files/data/public
sed -i 's#<script#<script nonce="{{ .CspNonce }}"#g' app/server/internal/files/data/public/index.html
'';
ldflags = [
"-s"
"-w"
"-X gopkg.c3c.cz/cv/app/server/internal/version.Tag=${version}"
"-X gopkg.c3c.cz/cv/app/server/internal/version.Commit=${self.rev or self.dirtyRev}"
"-X gopkg.c3c.cz/cv/app/server/internal/version.commitTime=${
builtins.toString (self.lastModified or 0)
}"
];
vendorHash = "sha256-44xcyVk5KcurQLkVJv1MeAj+Pfcu53664pvVgHdyv3E=";
};
image = pkgs.dockerTools.streamLayeredImage {
name = "cv";
tag = "current";
config = {
Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
Entrypoint = [ "${server}/bin/server" ];
};
};
}
);
devShells = nix.lib.forAllSystems (pkgs: {
default = nix.lib.mkDevenvShell {
inherit pkgs;
inputs = {
self = self;
nixpkgs = pkgs;
};
modules = [
{
packages = [
pkgs.go
pkgs.nodejs_22
];
enterShell = ''
cp -f ${nix.lib.eslint-config-file} ./app/frontend/eslint.shared.mjs
'';
scripts = {
# Override golangci-lint for vscode, because the extension incorrectly assumes usage of global binaries is preferred
golangci-lint = {
exec = ''
CMD=''${1:-}
if [[ "$CMD" == "run" ]]; then
shift
${pkgs.golangci-lint}/bin/golangci-lint run --config ${nix.lib.golangci-config-file} $@
else
${pkgs.golangci-lint}/bin/golangci-lint $@
fi
'';
};
lint = {
exec = ''
${nix.lib.cd_root}
${pkgs.golangci-lint}/bin/golangci-lint run --sort-results --out-format tab --config ${nix.lib.golangci-config-file} ./...
npm --prefix app/frontend run check
'';
};
fix = {
exec = ''
${nix.lib.cd_root}
nix fmt ./*.nix
${pkgs.golangci-lint}/bin/golangci-lint run --sort-results --out-format tab --config ${nix.lib.golangci-config-file} --fix --issues-exit-code 0 ./...
npm --prefix app/frontend run fix
'';
};
update-npm-deps-hash = {
exec = ''
${nix.lib.cd_root}
echo "Calculating npm deps"
# STDERR is poluted by the installed nodules, silencing
HASH=''$(${pkgs.prefetch-npm-deps}/bin/prefetch-npm-deps ./app/frontend/package-lock.json 2> /dev/null)
[[ ''$HASH = sha256* ]] && echo "Hash is ''$HASH"
[[ ''$HASH != sha256* ]] && echo "Failed" && exit 137
# Replace the first occurence of npmDepsHash with the new calculated hash in this file
sed -i "0,/npmDepsHash =/{s@npmDepsHash = .*@npmDepsHash = \"''$HASH\";@}" ./flake.nix
'';
};
dev = {
exec = ''
${nix.lib.cd_root}
npm --prefix ./app/frontend run dev
'';
};
};
}
];
};
});
};
}