├── .gitignore
├── README.md
├── angular-storybook
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── projects
│ └── components
│ │ ├── .eslintrc.json
│ │ ├── .storybook
│ │ ├── main.ts
│ │ ├── preview.ts
│ │ ├── tsconfig.json
│ │ └── typings.d.ts
│ │ ├── README.md
│ │ ├── documentation.json
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src
│ │ ├── lib
│ │ │ ├── components.component.spec.ts
│ │ │ ├── components.component.ts
│ │ │ ├── components.module.ts
│ │ │ ├── components.service.spec.ts
│ │ │ ├── components.service.ts
│ │ │ └── counter
│ │ │ │ ├── counter.component.css
│ │ │ │ ├── counter.component.html
│ │ │ │ ├── counter.component.spec.ts
│ │ │ │ ├── counter.component.stories.ts
│ │ │ │ └── counter.component.ts
│ │ ├── public-api.ts
│ │ └── stories
│ │ │ ├── .eslintrc.json
│ │ │ └── Introduction.mdx
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.lib.prod.json
│ │ └── tsconfig.spec.json
├── storybook.png
└── tsconfig.json
├── buildkit-parallelization
├── .dockerignore
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── Dockerfile
├── README.md
├── assets
│ └── pipeline.png
├── checkstyle.xml
├── docker-compose.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── org
│ │ └── example
│ │ ├── Main.java
│ │ ├── exception
│ │ ├── AppException.java
│ │ └── ConfigAppException.java
│ │ ├── service
│ │ └── SignService.java
│ │ └── servlet
│ │ └── SignServlet.java
│ └── test
│ └── java
│ └── org
│ └── example
│ └── service
│ └── SignServiceTest.java
├── db-for-testers
├── .gitignore
├── README.md
└── docker-entrypoint-initdb.d
│ ├── data.sql
│ └── schema.sql
├── graalvm-native-image-static
├── .gitignore
├── README.md
├── pom.xml
└── src
│ └── main
│ └── java
│ └── org
│ └── example
│ ├── Main.java
│ └── server
│ └── Server.java
└── gradle-application
├── .gitignore
├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
└── java
└── org
└── example
├── Main.java
├── dto
├── HealthResponseDto.java
└── MainResponseDto.java
└── serlvet
├── HealthServlet.java
└── MainServlet.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | NOTES.md
3 | Dockerfile.done
4 | docker-compose.done.yml
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DevOps Cases от Slurm
2 |
3 | В этом репозитории мы собрали интересные кейсы, с которыми сами регулярно сталкиваемся в работе
4 |
5 | Вы можете использовать эти кейсы для прокачки ваших скиллов по интересным вам направлениям
6 |
7 | Сами кейсы представлены в том виде, в котором они формулируются в реальной жизни, при этом:
8 | 1. Из кейсов убрано всё лишнее (т.е. и код, и сборка сильно упрощены)
9 | 2. Даны для удобства некоторые ссылки и временами даже подсказки
10 |
11 | **Важно**: это не набор задач для пошагового выполнения! Это именно набор кейсов, для разбора которых вам, возможно, придётся:
12 | - дополнительно изучить используемые в проекте технологии и их особенности
13 | - поковыряться в настройках и поэкспериментировать с ними
14 |
15 | ## Сборка контейнеров
16 |
17 | * [BuildKit Parallelization](buildkit-parallelization/)
18 | * [Angular Storybook](angular-storybook/)
19 | * [Gradle Application](gradle-application/)
20 | * [GraalVM Native Image - Static](graalvm-native-image-static/)
21 | * [DB for Testers](db-for-testers/)
22 |
--------------------------------------------------------------------------------
/angular-storybook/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/angular-storybook/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": [
4 | "projects/**/*"
5 | ],
6 | "overrides": [
7 | {
8 | "files": [
9 | "*.ts"
10 | ],
11 | "extends": [
12 | "eslint:recommended",
13 | "plugin:@typescript-eslint/recommended",
14 | "plugin:@angular-eslint/recommended",
15 | "plugin:@angular-eslint/template/process-inline-templates"
16 | ],
17 | "rules": {
18 | "@angular-eslint/directive-selector": [
19 | "error",
20 | {
21 | "type": "attribute",
22 | "prefix": "lib",
23 | "style": "camelCase"
24 | }
25 | ],
26 | "@angular-eslint/component-selector": [
27 | "error",
28 | {
29 | "type": "element",
30 | "prefix": "lib",
31 | "style": "kebab-case"
32 | }
33 | ]
34 | }
35 | },
36 | {
37 | "files": [
38 | "*.html"
39 | ],
40 | "extends": [
41 | "plugin:@angular-eslint/template/recommended"
42 | ],
43 | "rules": {}
44 | }
45 | ],
46 | "extends": [
47 | "plugin:storybook/recommended"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/angular-storybook/.gitignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | .dockerignore
3 | # See http://help.github.com/ignore-files/ for more about ignoring files.
4 | storybook-static/
5 |
6 | # Compiled output
7 | /dist
8 | /tmp
9 | /out-tsc
10 | /bazel-out
11 |
12 | # Node
13 | /node_modules
14 | npm-debug.log
15 | yarn-error.log
16 |
17 | # IDEs and editors
18 | .idea/
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # Visual Studio Code
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # Miscellaneous
35 | /.angular/cache
36 | .sass-cache/
37 | /connect.lock
38 | /coverage
39 | /libpeerconnection.log
40 | testem.log
41 | /typings
42 |
43 | # System files
44 | .DS_Store
45 | Thumbs.db
46 |
--------------------------------------------------------------------------------
/angular-storybook/README.md:
--------------------------------------------------------------------------------
1 | Теги: `Docker` `JS` `TypeScript` `Angular` `Storybook` `NPM`
2 |
3 | # Angular Storybook
4 |
5 | ## Задача
6 |
7 | Есть проект на Angular (исходники хранятся в этом каталоге), который представляет собой библиотеку компонентов одной из [дизайн-систем](https://tilda.education/courses/web-design/designsystem/)
8 |
9 | Необходимо упаковать витрину-компонентов в Docker-образ, используя в качестве базы конечного образа [Nginx](https://hub.docker.com/_/nginx) версии 1.22 (не alpine и не perl)
10 |
11 | Для промежуточных этапов (сборка, тестирование, линтинг) необходимо использовать [Node 18-buster-slim](https://hub.docker.com/_/node)
12 |
13 | **Важно**: нужно упаковывать не сам проект, а [витрину-компонентов на базе Storybook](https://storybook.js.org)
14 |
15 | ## Формулировка разработчика
16 |
17 | Разработчик сказал следующее* (дословно):
18 | > Мы делаем библиотеку компонентов на Angular, которая должна использоваться всеми проектами внутри компании
19 | >
20 | > Чтобы остальные понимали, как ею пользоваться, мы подготовили [Storybook](https://storybook.js.org)
21 | >
22 | > Теперь нам нужно упаковать всё в Docker так, чтобы при запуске Docker-контейнера люди могли увидеть Storybook
23 | >
24 | > Чтобы запустить Storybook локально, нужно выполнить команду `npm run storybook`, чтобы собрать его: `npm run build-storybook`
25 | >
26 | > Вот так примерно выглядит Storybook:
27 | >
28 | > 
29 | >
30 | > **Важно**:
31 | > 1. У нас есть авто-тесты, с UI они запускаются просто командой `npm test` (`Ctrl + C` для остановки)
32 | > 2. Мы придерживаемся определённого CodeStyle, поэтому проверяем стиль кода через `npm run lint`
33 | >
34 |
35 | *Лирическое отступление*: к сожалению, часто бывает, что разработчики знают, как использовать свои инструменты в режиме разработки, но не знают, как использовать "за пределами разработки", например, как опубликовать тот самый Storybook, поэтому с этим придётся разбираться самостоятельно
36 |
37 | Примечание*: мы обещали, что файлов будет немного (до 10 значащих), поэтому явно указываем, куда смотреть:
38 | 1. [`package.json`](package.json)
39 | 2. [`.gitignore`](.gitignore)
40 |
41 | В других файлах ничего смотреть не нужно, кроме того, не нужно вообще что-то в файлах проекта менять (кроме добавления собственного `Dockerfile` и `.dockerignore`)
42 |
43 | Что нужно сделать:
44 | > 1. Упаковать витрину в Docker-образ
45 | > 2. Выложить всё в виде публичного образа на GHCR* (GitHub Container Registry), чтобы мы могли сами затестить и переиспользовать
46 |
47 | Примечание*: GHCR приведён лишь в качестве примера, вы можете использовать любой реестр на собственное усмотрение
48 |
49 | **Важно**: мы хотим запускать и авто-тесты, и проверку стиля кода
50 |
51 | Чего не нужно делать (дословно):
52 | > Никаких `entrypoint.sh` и других sh-скриптов писать не нужно
53 | >
54 | > Переделывать приложение и конфигурационные файлы вроде `package.json` тоже не нужно, настраивайте запуск всего через:
55 | >
56 | > 1. Установку необходимых переменных окружения
57 | >
58 | > 2. Передачу флагов в соответствующие команды, например, в `npm test`
59 | >
60 |
61 | **Важно**: никакие конфигурации Karma и т.д. создавать не нужно (достаточно установки пары переменных окружения и передачи нескольких параметров)
62 |
63 | ## Реализация
64 |
65 | В качестве реализации CI/CD пайплайна и Docker Registry можно использовать любые, например (из облачных и бесплатных), [GitHub Actions](https://docs.github.com/en/actions) и [GitHub Packages](https://docs.github.com/packages)
66 |
--------------------------------------------------------------------------------
/angular-storybook/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "components": {
7 | "projectType": "library",
8 | "root": "projects/components",
9 | "sourceRoot": "projects/components/src",
10 | "prefix": "lib",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "project": "projects/components/ng-package.json"
16 | },
17 | "configurations": {
18 | "production": {
19 | "tsConfig": "projects/components/tsconfig.lib.prod.json"
20 | },
21 | "development": {
22 | "tsConfig": "projects/components/tsconfig.lib.json"
23 | }
24 | },
25 | "defaultConfiguration": "production"
26 | },
27 | "test": {
28 | "builder": "@angular-devkit/build-angular:karma",
29 | "options": {
30 | "tsConfig": "projects/components/tsconfig.spec.json",
31 | "polyfills": [
32 | "zone.js",
33 | "zone.js/testing"
34 | ]
35 | }
36 | },
37 | "lint": {
38 | "builder": "@angular-eslint/builder:lint",
39 | "options": {
40 | "lintFilePatterns": [
41 | "projects/components/**/*.ts",
42 | "projects/components/**/*.html"
43 | ]
44 | }
45 | },
46 | "storybook": {
47 | "builder": "@storybook/angular:start-storybook",
48 | "options": {
49 | "configDir": "projects/components/.storybook",
50 | "browserTarget": "components:build",
51 | "compodoc": true,
52 | "compodocArgs": [
53 | "-e",
54 | "json",
55 | "-d",
56 | "projects/components"
57 | ],
58 | "port": 6006
59 | }
60 | },
61 | "build-storybook": {
62 | "builder": "@storybook/angular:build-storybook",
63 | "options": {
64 | "configDir": "projects/components/.storybook",
65 | "browserTarget": "components:build",
66 | "compodoc": true,
67 | "compodocArgs": [
68 | "-e",
69 | "json",
70 | "-d",
71 | "projects/components"
72 | ],
73 | "outputDir": "storybook-static"
74 | }
75 | }
76 | }
77 | }
78 | },
79 | "cli": {
80 | "schematicCollections": [
81 | "@angular-eslint/schematics"
82 | ]
83 | }
84 | }
--------------------------------------------------------------------------------
/angular-storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "components-storybook",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "storybook": "ng run components:storybook",
12 | "build-storybook": "ng run components:build-storybook"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^15.2.0",
17 | "@angular/common": "^15.2.0",
18 | "@angular/compiler": "^15.2.0",
19 | "@angular/core": "^15.2.0",
20 | "@angular/forms": "^15.2.0",
21 | "@angular/platform-browser": "^15.2.0",
22 | "@angular/platform-browser-dynamic": "^15.2.0",
23 | "@angular/router": "^15.2.0",
24 | "rxjs": "~7.8.0",
25 | "tslib": "^2.3.0",
26 | "zone.js": "~0.12.0"
27 | },
28 | "devDependencies": {
29 | "@angular-devkit/build-angular": "^15.2.4",
30 | "@angular-eslint/builder": "15.2.1",
31 | "@angular-eslint/eslint-plugin": "15.2.1",
32 | "@angular-eslint/eslint-plugin-template": "15.2.1",
33 | "@angular-eslint/schematics": "15.2.1",
34 | "@angular-eslint/template-parser": "15.2.1",
35 | "@angular/cli": "~15.2.4",
36 | "@angular/compiler-cli": "^15.2.0",
37 | "@compodoc/compodoc": "^1.1.19",
38 | "@storybook/addon-essentials": "^7.0.0-rc.8",
39 | "@storybook/addon-interactions": "^7.0.0-rc.8",
40 | "@storybook/addon-links": "^7.0.0-rc.8",
41 | "@storybook/angular": "^7.0.0-rc.8",
42 | "@storybook/blocks": "^7.0.0-rc.8",
43 | "@storybook/testing-library": "^0.0.14-next.1",
44 | "@types/jasmine": "~4.3.0",
45 | "@typescript-eslint/eslint-plugin": "5.48.2",
46 | "@typescript-eslint/parser": "5.48.2",
47 | "eslint": "^8.33.0",
48 | "eslint-plugin-storybook": "^0.6.11",
49 | "jasmine-core": "~4.5.0",
50 | "karma": "~6.4.0",
51 | "karma-chrome-launcher": "~3.1.0",
52 | "karma-coverage": "~2.2.0",
53 | "karma-jasmine": "~5.1.0",
54 | "karma-jasmine-html-reporter": "~2.0.0",
55 | "ng-packagr": "^15.2.2",
56 | "react": "^18.2.0",
57 | "react-dom": "^18.2.0",
58 | "storybook": "^7.0.0-rc.8",
59 | "typescript": "~4.9.4"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*"
5 | ],
6 | "overrides": [
7 | {
8 | "files": [
9 | "*.ts"
10 | ],
11 | "rules": {
12 | "@angular-eslint/directive-selector": [
13 | "error",
14 | {
15 | "type": "attribute",
16 | "prefix": "lib",
17 | "style": "camelCase"
18 | }
19 | ],
20 | "@angular-eslint/component-selector": [
21 | "error",
22 | {
23 | "type": "element",
24 | "prefix": "lib",
25 | "style": "kebab-case"
26 | }
27 | ]
28 | }
29 | },
30 | {
31 | "files": [
32 | "*.html"
33 | ],
34 | "rules": {}
35 | }
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from "@storybook/angular";
2 | const config: StorybookConfig = {
3 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
4 | addons: [
5 | "@storybook/addon-links",
6 | "@storybook/addon-essentials",
7 | "@storybook/addon-interactions",
8 | ],
9 | framework: {
10 | name: "@storybook/angular",
11 | options: {},
12 | },
13 | docs: {
14 | autodocs: "tag",
15 | },
16 | core: {
17 | disableTelemetry: true,
18 | },
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import type { Preview } from "@storybook/angular";
2 | import { setCompodocJson } from "@storybook/addon-docs/angular";
3 | import docJson from "../documentation.json";
4 | setCompodocJson(docJson);
5 |
6 | const preview: Preview = {
7 | parameters: {
8 | actions: { argTypesRegex: "^on[A-Z].*" },
9 | controls: {
10 | matchers: {
11 | color: /(background|color)$/i,
12 | date: /Date$/,
13 | },
14 | },
15 | },
16 | };
17 |
18 | export default preview;
19 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/.storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.lib.json",
3 | "compilerOptions": {
4 | "types": ["node"],
5 | "allowSyntheticDefaultImports": true,
6 | "resolveJsonModule": true
7 | },
8 | "exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
9 | "include": ["../src/**/*", "./preview.ts"],
10 | "files": ["./typings.d.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/.storybook/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.md' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/README.md:
--------------------------------------------------------------------------------
1 | # Components
2 |
3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.2.0.
4 |
5 | ## Code scaffolding
6 |
7 | Run `ng generate component component-name --project components` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project components`.
8 | > Note: Don't forget to add `--project components` or else it will be added to the default project in your `angular.json` file.
9 |
10 | ## Build
11 |
12 | Run `ng build components` to build the project. The build artifacts will be stored in the `dist/` directory.
13 |
14 | ## Publishing
15 |
16 | After building your library with `ng build components`, go to the dist folder `cd dist/components` and run `npm publish`.
17 |
18 | ## Running unit tests
19 |
20 | Run `ng test components` to execute the unit tests via [Karma](https://karma-runner.github.io).
21 |
22 | ## Further help
23 |
24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
25 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/documentation.json:
--------------------------------------------------------------------------------
1 | {
2 | "pipes": [],
3 | "interfaces": [],
4 | "injectables": [
5 | {
6 | "name": "ComponentsService",
7 | "id": "injectable-ComponentsService-1a68e5ab8060ac18996868bd337f39aafb46491a3144134c587d69dfd820cf07ba62295c1f88df06847e3ef997966d1275f1d740fea7c0054af98562a751c5c8",
8 | "file": "projects/components/src/lib/components.service.ts",
9 | "properties": [],
10 | "methods": [],
11 | "deprecated": false,
12 | "deprecationMessage": "",
13 | "description": "",
14 | "rawdescription": "\n",
15 | "sourceCode": "import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ComponentsService {\n}\n",
16 | "type": "injectable"
17 | }
18 | ],
19 | "guards": [],
20 | "interceptors": [],
21 | "classes": [],
22 | "directives": [],
23 | "components": [
24 | {
25 | "name": "ComponentsComponent",
26 | "id": "component-ComponentsComponent-9ab62237a9b0bb4002405ad11127778bede646e1f7acafd99d9501b37bd21cd6b3ef9f89e57bab0c22f531acb1df42d8f2e139a6066634bf5afca0f4c9fe0106",
27 | "file": "projects/components/src/lib/components.component.ts",
28 | "encapsulation": [],
29 | "entryComponents": [],
30 | "inputs": [],
31 | "outputs": [],
32 | "providers": [],
33 | "selector": "lib-components",
34 | "styleUrls": [],
35 | "styles": [],
36 | "template": "
\n components works!\n
\n",
37 | "templateUrl": [],
38 | "viewProviders": [],
39 | "inputsClass": [],
40 | "outputsClass": [],
41 | "propertiesClass": [],
42 | "methodsClass": [],
43 | "deprecated": false,
44 | "deprecationMessage": "",
45 | "hostBindings": [],
46 | "hostListeners": [],
47 | "description": "",
48 | "rawdescription": "\n",
49 | "type": "component",
50 | "sourceCode": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'lib-components',\n template: `\n \n components works!\n
\n `,\n styles: [\n ]\n})\nexport class ComponentsComponent {\n\n}\n",
51 | "assetsDirs": [],
52 | "styleUrlsData": "",
53 | "stylesData": ""
54 | },
55 | {
56 | "name": "CounterComponent",
57 | "id": "component-CounterComponent-dec7b5f254e9a70b3d288a731a015e80650bffd879b0a1e82602958e0cf740d2879ae92e4b810aa8e3b7927c7146a7f7f768bc6f8fef3630e2f9b766a0945adf",
58 | "file": "projects/components/src/lib/counter/counter.component.ts",
59 | "encapsulation": [],
60 | "entryComponents": [],
61 | "inputs": [],
62 | "outputs": [],
63 | "providers": [],
64 | "selector": "lib-counter",
65 | "styleUrls": [
66 | "./counter.component.css"
67 | ],
68 | "styles": [],
69 | "templateUrl": [
70 | "./counter.component.html"
71 | ],
72 | "viewProviders": [],
73 | "inputsClass": [
74 | {
75 | "name": "disabled",
76 | "defaultValue": "false",
77 | "deprecated": false,
78 | "deprecationMessage": "",
79 | "line": 14,
80 | "type": "boolean",
81 | "decorators": []
82 | },
83 | {
84 | "name": "prefix",
85 | "defaultValue": "'lib-counter'",
86 | "deprecated": false,
87 | "deprecationMessage": "",
88 | "line": 9,
89 | "type": "string",
90 | "decorators": []
91 | },
92 | {
93 | "name": "primary",
94 | "defaultValue": "true",
95 | "deprecated": false,
96 | "deprecationMessage": "",
97 | "line": 10,
98 | "type": "boolean",
99 | "decorators": []
100 | },
101 | {
102 | "name": "size",
103 | "defaultValue": "'medium'",
104 | "deprecated": false,
105 | "deprecationMessage": "",
106 | "line": 11,
107 | "type": "\"small\" | \"medium\" | \"large\"",
108 | "decorators": []
109 | },
110 | {
111 | "name": "testid",
112 | "defaultValue": "''",
113 | "deprecated": false,
114 | "deprecationMessage": "",
115 | "line": 12,
116 | "type": "string",
117 | "decorators": []
118 | },
119 | {
120 | "name": "type",
121 | "defaultValue": "''",
122 | "deprecated": false,
123 | "deprecationMessage": "",
124 | "line": 13,
125 | "type": "string",
126 | "decorators": []
127 | },
128 | {
129 | "name": "value",
130 | "defaultValue": "0",
131 | "deprecated": false,
132 | "deprecationMessage": "",
133 | "line": 15,
134 | "type": "number",
135 | "decorators": []
136 | }
137 | ],
138 | "outputsClass": [
139 | {
140 | "name": "increase",
141 | "defaultValue": "new EventEmitter()",
142 | "deprecated": false,
143 | "deprecationMessage": "",
144 | "line": 16,
145 | "type": "EventEmitter"
146 | }
147 | ],
148 | "propertiesClass": [],
149 | "methodsClass": [
150 | {
151 | "name": "onClick",
152 | "args": [
153 | {
154 | "name": "$event",
155 | "type": "Event",
156 | "deprecated": false,
157 | "deprecationMessage": ""
158 | }
159 | ],
160 | "optional": false,
161 | "returnType": "void",
162 | "typeParameters": [],
163 | "line": 22,
164 | "deprecated": false,
165 | "deprecationMessage": "",
166 | "jsdoctags": [
167 | {
168 | "name": "$event",
169 | "type": "Event",
170 | "deprecated": false,
171 | "deprecationMessage": "",
172 | "tagName": {
173 | "text": "param"
174 | }
175 | }
176 | ]
177 | }
178 | ],
179 | "deprecated": false,
180 | "deprecationMessage": "",
181 | "hostBindings": [],
182 | "hostListeners": [],
183 | "description": "",
184 | "rawdescription": "\n",
185 | "type": "component",
186 | "sourceCode": "import { Component, EventEmitter, Input, Output } from '@angular/core';\n\n@Component({\n selector: 'lib-counter',\n templateUrl: './counter.component.html',\n styleUrls: ['./counter.component.css']\n})\nexport class CounterComponent {\n @Input() prefix = 'lib-counter';\n @Input() primary = true;\n @Input() size : 'small' | 'medium' | 'large' = 'medium';\n @Input() testid = '';\n @Input() type = '';\n @Input() disabled = false;\n @Input() value = 0;\n @Output() increase = new EventEmitter();\n\n public get classes(): string[] {\n return [this.prefix, `${this.prefix}--${this.size}`, `${this.prefix}--${this.primary ? 'primary' : 'secondary'}`];\n }\n\n onClick($event: Event) {\n $event.preventDefault();\n this.value++;\n console.log('click');\n this.increase.emit(this.value);\n }\n}\n",
187 | "assetsDirs": [],
188 | "styleUrlsData": [
189 | {
190 | "data": ".lib-counter {\n font-family: Helvetica, Arial, sans-serif;\n font-weight: 700;\n border-radius: 5px;\n cursor: pointer;\n display: inline-block;\n line-height: 1;\n}\n.lib-counter--primary {\n color: white;\n background-color: #8a73fc;\n border: 1px solid #8a73fc;\n}\n.lib-counter--secondary {\n color: #172b4d;\n background-color: transparent;\n border: 1px solid #172b4d;\n}\n.lib-counter--small {\n font-size: 12px;\n padding: 10px 16px;\n}\n.lib-counter--medium {\n font-size: 14px;\n padding: 11px 20px;\n}\n.lib-counter--large {\n font-size: 16px;\n padding: 12px 24px;\n}\n",
191 | "styleUrl": "./counter.component.css"
192 | }
193 | ],
194 | "stylesData": "",
195 | "accessors": {
196 | "classes": {
197 | "name": "classes",
198 | "getSignature": {
199 | "name": "classes",
200 | "type": "[]",
201 | "returnType": "string[]",
202 | "line": 18
203 | }
204 | }
205 | },
206 | "templateData": "\n\t\n"
207 | }
208 | ],
209 | "modules": [
210 | {
211 | "name": "ComponentsModule",
212 | "id": "module-ComponentsModule-924be5c2c6949aeeed9a9848432fce6586816afa3ae2cd3df16a25531ff649d8148ee6c33be3454bf97abde0ea1c5a746d39eb875b0e0fdd60687b3e45c430bd",
213 | "description": "",
214 | "deprecationMessage": "",
215 | "deprecated": false,
216 | "file": "projects/components/src/lib/components.module.ts",
217 | "methods": [],
218 | "sourceCode": "import { NgModule } from '@angular/core';\nimport { ComponentsComponent } from './components.component';\nimport { CounterComponent } from './counter/counter.component';\n\n\n\n@NgModule({\n declarations: [\n ComponentsComponent,\n CounterComponent\n ],\n imports: [\n ],\n exports: [\n ComponentsComponent\n ]\n})\nexport class ComponentsModule { }\n",
219 | "children": [
220 | {
221 | "type": "providers",
222 | "elements": []
223 | },
224 | {
225 | "type": "declarations",
226 | "elements": [
227 | {
228 | "name": "ComponentsComponent"
229 | },
230 | {
231 | "name": "CounterComponent"
232 | }
233 | ]
234 | },
235 | {
236 | "type": "imports",
237 | "elements": []
238 | },
239 | {
240 | "type": "exports",
241 | "elements": [
242 | {
243 | "name": "ComponentsComponent"
244 | }
245 | ]
246 | },
247 | {
248 | "type": "bootstrap",
249 | "elements": []
250 | },
251 | {
252 | "type": "classes",
253 | "elements": []
254 | }
255 | ]
256 | }
257 | ],
258 | "miscellaneous": {
259 | "variables": [
260 | {
261 | "name": "LargeCounter",
262 | "ctype": "miscellaneous",
263 | "subtype": "variable",
264 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
265 | "deprecated": false,
266 | "deprecationMessage": "",
267 | "type": "Story",
268 | "defaultValue": "{\n args: {\n size: 'large',\n value: 999,\n },\n}"
269 | },
270 | {
271 | "name": "meta",
272 | "ctype": "miscellaneous",
273 | "subtype": "variable",
274 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
275 | "deprecated": false,
276 | "deprecationMessage": "",
277 | "type": "Meta",
278 | "defaultValue": "{\n title: 'Example/CounterComponent',\n component: CounterComponent,\n tags: ['autodocs'],\n render: (args: CounterComponent) => ({\n props: {\n ...args,\n },\n }),\n}"
279 | },
280 | {
281 | "name": "preview",
282 | "ctype": "miscellaneous",
283 | "subtype": "variable",
284 | "file": "projects/components/.storybook/preview.ts",
285 | "deprecated": false,
286 | "deprecationMessage": "",
287 | "type": "Preview",
288 | "defaultValue": "{\n parameters: {\n actions: { argTypesRegex: \"^on[A-Z].*\" },\n controls: {\n matchers: {\n color: /(background|color)$/i,\n date: /Date$/,\n },\n },\n },\n}"
289 | },
290 | {
291 | "name": "PrimaryCounter",
292 | "ctype": "miscellaneous",
293 | "subtype": "variable",
294 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
295 | "deprecated": false,
296 | "deprecationMessage": "",
297 | "type": "Story",
298 | "defaultValue": "{\n args: {\n primary: true,\n value: 1,\n },\n}"
299 | },
300 | {
301 | "name": "SecondaryCounter",
302 | "ctype": "miscellaneous",
303 | "subtype": "variable",
304 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
305 | "deprecated": false,
306 | "deprecationMessage": "",
307 | "type": "Story",
308 | "defaultValue": "{\n args: {\n primary: false,\n value: 1,\n },\n}"
309 | },
310 | {
311 | "name": "SmallCounter",
312 | "ctype": "miscellaneous",
313 | "subtype": "variable",
314 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
315 | "deprecated": false,
316 | "deprecationMessage": "",
317 | "type": "Story",
318 | "defaultValue": "{\n args: {\n size: 'small',\n value: 9,\n },\n}"
319 | }
320 | ],
321 | "functions": [],
322 | "typealiases": [
323 | {
324 | "name": "Story",
325 | "ctype": "miscellaneous",
326 | "subtype": "typealias",
327 | "rawtype": "StoryObj",
328 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
329 | "deprecated": false,
330 | "deprecationMessage": "",
331 | "description": "",
332 | "kind": 177
333 | }
334 | ],
335 | "enumerations": [],
336 | "groupedVariables": {
337 | "projects/components/src/lib/counter/counter.component.stories.ts": [
338 | {
339 | "name": "LargeCounter",
340 | "ctype": "miscellaneous",
341 | "subtype": "variable",
342 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
343 | "deprecated": false,
344 | "deprecationMessage": "",
345 | "type": "Story",
346 | "defaultValue": "{\n args: {\n size: 'large',\n value: 999,\n },\n}"
347 | },
348 | {
349 | "name": "meta",
350 | "ctype": "miscellaneous",
351 | "subtype": "variable",
352 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
353 | "deprecated": false,
354 | "deprecationMessage": "",
355 | "type": "Meta",
356 | "defaultValue": "{\n title: 'Example/CounterComponent',\n component: CounterComponent,\n tags: ['autodocs'],\n render: (args: CounterComponent) => ({\n props: {\n ...args,\n },\n }),\n}"
357 | },
358 | {
359 | "name": "PrimaryCounter",
360 | "ctype": "miscellaneous",
361 | "subtype": "variable",
362 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
363 | "deprecated": false,
364 | "deprecationMessage": "",
365 | "type": "Story",
366 | "defaultValue": "{\n args: {\n primary: true,\n value: 1,\n },\n}"
367 | },
368 | {
369 | "name": "SecondaryCounter",
370 | "ctype": "miscellaneous",
371 | "subtype": "variable",
372 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
373 | "deprecated": false,
374 | "deprecationMessage": "",
375 | "type": "Story",
376 | "defaultValue": "{\n args: {\n primary: false,\n value: 1,\n },\n}"
377 | },
378 | {
379 | "name": "SmallCounter",
380 | "ctype": "miscellaneous",
381 | "subtype": "variable",
382 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
383 | "deprecated": false,
384 | "deprecationMessage": "",
385 | "type": "Story",
386 | "defaultValue": "{\n args: {\n size: 'small',\n value: 9,\n },\n}"
387 | }
388 | ],
389 | "projects/components/.storybook/preview.ts": [
390 | {
391 | "name": "preview",
392 | "ctype": "miscellaneous",
393 | "subtype": "variable",
394 | "file": "projects/components/.storybook/preview.ts",
395 | "deprecated": false,
396 | "deprecationMessage": "",
397 | "type": "Preview",
398 | "defaultValue": "{\n parameters: {\n actions: { argTypesRegex: \"^on[A-Z].*\" },\n controls: {\n matchers: {\n color: /(background|color)$/i,\n date: /Date$/,\n },\n },\n },\n}"
399 | }
400 | ]
401 | },
402 | "groupedFunctions": {},
403 | "groupedEnumerations": {},
404 | "groupedTypeAliases": {
405 | "projects/components/src/lib/counter/counter.component.stories.ts": [
406 | {
407 | "name": "Story",
408 | "ctype": "miscellaneous",
409 | "subtype": "typealias",
410 | "rawtype": "StoryObj",
411 | "file": "projects/components/src/lib/counter/counter.component.stories.ts",
412 | "deprecated": false,
413 | "deprecationMessage": "",
414 | "description": "",
415 | "kind": 177
416 | }
417 | ]
418 | }
419 | },
420 | "routes": [],
421 | "coverage": {
422 | "count": 0,
423 | "status": "low",
424 | "files": [
425 | {
426 | "filePath": "projects/components/.storybook/preview.ts",
427 | "type": "variable",
428 | "linktype": "miscellaneous",
429 | "linksubtype": "variable",
430 | "name": "preview",
431 | "coveragePercent": 0,
432 | "coverageCount": "0/1",
433 | "status": "low"
434 | },
435 | {
436 | "filePath": "projects/components/src/lib/components.component.ts",
437 | "type": "component",
438 | "linktype": "component",
439 | "name": "ComponentsComponent",
440 | "coveragePercent": 0,
441 | "coverageCount": "0/1",
442 | "status": "low"
443 | },
444 | {
445 | "filePath": "projects/components/src/lib/components.service.ts",
446 | "type": "injectable",
447 | "linktype": "injectable",
448 | "name": "ComponentsService",
449 | "coveragePercent": 0,
450 | "coverageCount": "0/1",
451 | "status": "low"
452 | },
453 | {
454 | "filePath": "projects/components/src/lib/counter/counter.component.stories.ts",
455 | "type": "variable",
456 | "linktype": "miscellaneous",
457 | "linksubtype": "variable",
458 | "name": "LargeCounter",
459 | "coveragePercent": 0,
460 | "coverageCount": "0/1",
461 | "status": "low"
462 | },
463 | {
464 | "filePath": "projects/components/src/lib/counter/counter.component.stories.ts",
465 | "type": "variable",
466 | "linktype": "miscellaneous",
467 | "linksubtype": "variable",
468 | "name": "meta",
469 | "coveragePercent": 0,
470 | "coverageCount": "0/1",
471 | "status": "low"
472 | },
473 | {
474 | "filePath": "projects/components/src/lib/counter/counter.component.stories.ts",
475 | "type": "variable",
476 | "linktype": "miscellaneous",
477 | "linksubtype": "variable",
478 | "name": "PrimaryCounter",
479 | "coveragePercent": 0,
480 | "coverageCount": "0/1",
481 | "status": "low"
482 | },
483 | {
484 | "filePath": "projects/components/src/lib/counter/counter.component.stories.ts",
485 | "type": "variable",
486 | "linktype": "miscellaneous",
487 | "linksubtype": "variable",
488 | "name": "SecondaryCounter",
489 | "coveragePercent": 0,
490 | "coverageCount": "0/1",
491 | "status": "low"
492 | },
493 | {
494 | "filePath": "projects/components/src/lib/counter/counter.component.stories.ts",
495 | "type": "variable",
496 | "linktype": "miscellaneous",
497 | "linksubtype": "variable",
498 | "name": "SmallCounter",
499 | "coveragePercent": 0,
500 | "coverageCount": "0/1",
501 | "status": "low"
502 | },
503 | {
504 | "filePath": "projects/components/src/lib/counter/counter.component.ts",
505 | "type": "component",
506 | "linktype": "component",
507 | "name": "CounterComponent",
508 | "coveragePercent": 0,
509 | "coverageCount": "0/10",
510 | "status": "low"
511 | }
512 | ]
513 | }
514 | }
--------------------------------------------------------------------------------
/angular-storybook/projects/components/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/components",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
--------------------------------------------------------------------------------
/angular-storybook/projects/components/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "components",
3 | "version": "0.0.1",
4 | "peerDependencies": {
5 | "@angular/common": "^15.2.0",
6 | "@angular/core": "^15.2.0"
7 | },
8 | "dependencies": {
9 | "tslib": "^2.3.0"
10 | },
11 | "sideEffects": false
12 | }
13 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/components.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ComponentsComponent } from './components.component';
4 |
5 | describe('ComponentsComponent', () => {
6 | let component: ComponentsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ComponentsComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(ComponentsComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/components.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'lib-components',
5 | template: `
6 |
7 | components works!
8 |
9 | `,
10 | styles: [
11 | ]
12 | })
13 | export class ComponentsComponent {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/components.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { ComponentsComponent } from './components.component';
3 | import { CounterComponent } from './counter/counter.component';
4 |
5 |
6 |
7 | @NgModule({
8 | declarations: [
9 | ComponentsComponent,
10 | CounterComponent
11 | ],
12 | imports: [
13 | ],
14 | exports: [
15 | ComponentsComponent
16 | ]
17 | })
18 | export class ComponentsModule { }
19 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/components.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { ComponentsService } from './components.service';
4 |
5 | describe('ComponentsService', () => {
6 | let service: ComponentsService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(ComponentsService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/components.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class ComponentsService {
7 | }
8 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/counter/counter.component.css:
--------------------------------------------------------------------------------
1 | .lib-counter {
2 | font-family: Helvetica, Arial, sans-serif;
3 | font-weight: 700;
4 | border-radius: 5px;
5 | cursor: pointer;
6 | display: inline-block;
7 | line-height: 1;
8 | }
9 | .lib-counter--primary {
10 | color: white;
11 | background-color: #8a73fc;
12 | border: 1px solid #8a73fc;
13 | }
14 | .lib-counter--secondary {
15 | color: #172b4d;
16 | background-color: transparent;
17 | border: 1px solid #172b4d;
18 | }
19 | .lib-counter--small {
20 | font-size: 12px;
21 | padding: 10px 16px;
22 | }
23 | .lib-counter--medium {
24 | font-size: 14px;
25 | padding: 11px 20px;
26 | }
27 | .lib-counter--large {
28 | font-size: 16px;
29 | padding: 12px 24px;
30 | }
31 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/counter/counter.component.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/counter/counter.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CounterComponent } from './counter.component';
4 |
5 | describe('CounterComponent', () => {
6 | let component: CounterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ CounterComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(CounterComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/counter/counter.component.stories.ts:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/angular';
2 | import { CounterComponent } from './counter.component';
3 |
4 | const meta: Meta = {
5 | title: 'Example/CounterComponent',
6 | component: CounterComponent,
7 | tags: ['autodocs'],
8 | render: (args: CounterComponent) => ({
9 | props: {
10 | ...args,
11 | },
12 | }),
13 | };
14 |
15 | export default meta;
16 | type Story = StoryObj;
17 |
18 | export const PrimaryCounter: Story = {
19 | args: {
20 | primary: true,
21 | value: 1,
22 | },
23 | };
24 |
25 | export const SecondaryCounter: Story = {
26 | args: {
27 | primary: false,
28 | value: 1,
29 | },
30 | };
31 |
32 | export const LargeCounter: Story = {
33 | args: {
34 | size: 'large',
35 | value: 999,
36 | },
37 | };
38 |
39 | export const SmallCounter: Story = {
40 | args: {
41 | size: 'small',
42 | value: 9,
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/lib/counter/counter.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'lib-counter',
5 | templateUrl: './counter.component.html',
6 | styleUrls: ['./counter.component.css']
7 | })
8 | export class CounterComponent {
9 | @Input() prefix = 'lib-counter';
10 | @Input() primary = true;
11 | @Input() size : 'small' | 'medium' | 'large' = 'medium';
12 | @Input() testid = '';
13 | @Input() type = '';
14 | @Input() disabled = false;
15 | @Input() value = 0;
16 | @Output() increase = new EventEmitter();
17 |
18 | public get classes(): string[] {
19 | return [this.prefix, `${this.prefix}--${this.size}`, `${this.prefix}--${this.primary ? 'primary' : 'secondary'}`];
20 | }
21 |
22 | onClick($event: Event) {
23 | $event.preventDefault();
24 | this.value++;
25 | console.log('click');
26 | this.increase.emit(this.value);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of components
3 | */
4 |
5 | export * from './lib/components.service';
6 | export * from './lib/components.component';
7 | export * from './lib/components.module';
8 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/stories/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "@typescript-eslint/consistent-type-imports": ["error", { "disallowTypeAnnotations": false }]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/src/stories/Introduction.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/blocks';
2 |
3 |
4 |
5 | # Welcome to Storybook
6 |
7 | Этот Storybook демонстрирует использование нашей библиотеки компонентов
--------------------------------------------------------------------------------
/angular-storybook/projects/components/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/lib",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": []
10 | },
11 | "exclude": [
12 | "**/*.spec.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "compilationMode": "partial"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/angular-storybook/projects/components/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "**/*.spec.ts",
12 | "**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/angular-storybook/storybook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slurmio/devopscases/09b9f14317068252642b162b73b30279e20a2925/angular-storybook/storybook.png
--------------------------------------------------------------------------------
/angular-storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "paths": {
7 | "components": [
8 | "dist/components"
9 | ]
10 | },
11 | "outDir": "./dist/out-tsc",
12 | "forceConsistentCasingInFileNames": true,
13 | "strict": true,
14 | "noImplicitOverride": true,
15 | "noPropertyAccessFromIndexSignature": true,
16 | "noImplicitReturns": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "sourceMap": true,
19 | "declaration": false,
20 | "downlevelIteration": true,
21 | "experimentalDecorators": true,
22 | "moduleResolution": "node",
23 | "importHelpers": true,
24 | "target": "ES2022",
25 | "module": "ES2022",
26 | "useDefineForClassFields": false,
27 | "lib": [
28 | "ES2022",
29 | "dom"
30 | ]
31 | },
32 | "angularCompilerOptions": {
33 | "enableI18nLegacyMessageIdFormat": false,
34 | "strictInjectionParameters": true,
35 | "strictInputAccessModifiers": true,
36 | "strictTemplates": true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/buildkit-parallelization/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile*
2 | docker-compose*.yml
3 |
4 | assets/
5 | target/
6 | !.mvn/wrapper/maven-wrapper.jar
7 | !**/src/main/**/target/
8 | !**/src/test/**/target/
9 |
10 | ### IntelliJ IDEA ###
11 | .idea
12 | *.iws
13 | *.iml
14 | *.ipr
15 |
16 | ### Eclipse ###
17 | .apt_generated
18 | .classpath
19 | .factorypath
20 | .project
21 | .settings
22 | .springBeans
23 | .sts4-cache
24 |
25 | ### NetBeans ###
26 | /nbproject/private/
27 | /nbbuild/
28 | /dist/
29 | /nbdist/
30 | /.nb-gradle/
31 | build/
32 | !**/src/main/**/build/
33 | !**/src/test/**/build/
34 |
35 | ### VS Code ###
36 | .vscode/
37 |
38 | ### Mac OS, Windows ###
39 | .DS_Store
40 | Thumbs.db*
41 |
--------------------------------------------------------------------------------
/buildkit-parallelization/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea
8 | *.iws
9 | *.iml
10 | *.ipr
11 |
12 | ### Eclipse ###
13 | .apt_generated
14 | .classpath
15 | .factorypath
16 | .project
17 | .settings
18 | .springBeans
19 | .sts4-cache
20 |
21 | ### NetBeans ###
22 | /nbproject/private/
23 | /nbbuild/
24 | /dist/
25 | /nbdist/
26 | /.nb-gradle/
27 | build/
28 | !**/src/main/**/build/
29 | !**/src/test/**/build/
30 |
31 | ### VS Code ###
32 | .vscode/
33 |
34 | ### Mac OS, Windows ###
35 | .DS_Store
36 | Thumbs.db*
--------------------------------------------------------------------------------
/buildkit-parallelization/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slurmio/devopscases/09b9f14317068252642b162b73b30279e20a2925/buildkit-parallelization/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/buildkit-parallelization/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip
18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
19 |
--------------------------------------------------------------------------------
/buildkit-parallelization/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM eclipse-temurin@sha256:831f00e903bf888787b8ffd1bf5252e5903e1b19d5bc17f7e5b9761ffec11785 AS builder
2 | WORKDIR /opt/build
3 | COPY . .
4 | RUN ./mvnw --no-transfer-progress --batch-mode verify
5 |
6 | FROM eclipse-temurin@sha256:831f00e903bf888787b8ffd1bf5252e5903e1b19d5bc17f7e5b9761ffec11785 AS release
7 | WORKDIR /opt/app
8 | COPY --from=builder /opt/build/target/app.jar .
9 |
10 | ENV APP_SIGN_ALGO HmacSHA256
11 | ENV APP_SIGN_KEY 073b9ce8277c4d70dba2edf33e80f6a2bde0dbd7e5c9d149ccc20ba8b125129b
12 |
13 | USER 1000
14 |
15 | EXPOSE 8080
16 | CMD ["java", "-jar", "app.jar"]
--------------------------------------------------------------------------------
/buildkit-parallelization/README.md:
--------------------------------------------------------------------------------
1 | Теги: `Docker` `Java` `Maven`
2 |
3 | # BuildKit Parallelization
4 |
5 | Разработчики проекта хотят ускорить сборку и прохождение проверок (авто-тесты, проверка CodeStyle, зависимостей и т.д.) и обратились к вам, как к эксперту по Docker, системам сборки и прочему (включая выстраивание оптимальных процессов)
6 |
7 | Мотивация: авто-тесты, как и проверка зависимостей на уязвимости (и другие активности), могут проходить достаточно долго, поэтому почему бы их не распараллелить
8 |
9 | **Что нужно сделать**: используя возможности [BuildKit](https://docs.docker.com/build/buildkit/) распараллелить сборку (компиляцию исходников и сборку Uber JAR) и служебные задачи вроде проверки CodeStyle, Unit-тестов и т.д.
10 |
11 | Желаемый вид:
12 |
13 | 
14 |
15 | **Важно**: `pom.xml` редактировать нельзя
16 |
17 | *Q*: почему инструменты верификации нужно запускать после компиляции?
18 |
19 | *A*: некоторые из них (например, SpotBugs) работают на уровне байт-кода, кроме того, проверять стиль кодирования имеет смысл только в случае, если код хотя бы компилируется, поэтому мы упростили задачу и не предлагаем вам детально разбираться в том, что конкретный плагин требует (только исходников или ещё и байт-код)
20 |
21 | ## Инструкции по [сборке проекта](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html)
22 |
23 | Текущая сборка описана в файле [`Dockerfile`](Dockerfile), сборка производится командой:
24 |
25 | ```shell
26 | docker buildx build .
27 | ```
28 |
29 | Используемые переменные окружения:
30 | * `APP_SIGN_ALGO` — алгоритм генерации [HMAC](https://www.rfc-editor.org/rfc/rfc2104) (в терминах разработчика — "подписи")
31 | * `APP_SIGN_KEY` — ключ для генерации [HMAC](https://www.rfc-editor.org/rfc/rfc2104) (в терминах разработчика — "подписи")
32 |
33 | Используемый порт: `8080`
34 |
35 | Команда для тестирования:
36 | ```shell
37 | curl -X POST http://localhost:8080 -d 'secret'
38 | ```
39 |
40 | Ожидаемый ответ:
41 | ```shell
42 | b4a5151c4d6939f8df7febe32ac94c3ce0f61112cbe1d8df0e51d7f2c4d9618b
43 | ```
44 |
45 | ## Подсказки
46 |
47 | Если очень-очень верхнеуровнево, то:
48 |
49 | 1. `./mvnw package` — компиляция, Unit-тесты, сборка Uber JAR
50 | 2. `./mvnw verify` — `package` + проверка CodeStyle, проверка уязвимых зависимостей, поиск багов
51 |
52 | Детали см. в [Maven Default Lifecycle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle)
53 |
54 | **Важно**: убедитесь, что вынесенные в "параллельные" stage активности действительно отрабатывают
55 |
56 |
57 | Спойлеры
58 |
59 | Вполне возможно, что BuildKit оптимизирует сборку таким образом, что при определённых условиях "параллельные" stage запускаться не будут
60 |
61 |
62 | ## Реализация
63 |
64 | В качестве реализации CI/CD пайплайна и Docker Registry можно использовать любые, например (из облачных и бесплатных), [GitHub Actions](https://docs.github.com/en/actions) и [GitHub Packages](https://docs.github.com/packages)
65 |
66 | # Разборы
67 |
68 | * 24.10.2023 – Максим Суслов ([Видео на YouTube](https://www.youtube.com/watch?v=VVSLdUfa_Js), [Репо на GitHub](https://github.com/maxx27/slurm-devopscases/tree/buildkit-parallelization-solution))
69 |
--------------------------------------------------------------------------------
/buildkit-parallelization/assets/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slurmio/devopscases/09b9f14317068252642b162b73b30279e20a2925/buildkit-parallelization/assets/pipeline.png
--------------------------------------------------------------------------------
/buildkit-parallelization/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
71 |
72 |
73 |
75 |
76 |
77 |
83 |
84 |
85 |
86 |
89 |
90 |
91 |
92 |
93 |
97 |
98 |
99 |
100 |
101 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
119 |
122 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
170 |
171 |
172 |
174 |
176 |
177 |
178 |
179 |
181 |
182 |
183 |
184 |
186 |
187 |
188 |
189 |
191 |
192 |
193 |
194 |
196 |
197 |
198 |
199 |
201 |
202 |
203 |
204 |
206 |
207 |
208 |
209 |
211 |
212 |
213 |
214 |
216 |
217 |
218 |
219 |
221 |
222 |
223 |
224 |
226 |
227 |
228 |
229 |
231 |
232 |
233 |
234 |
236 |
238 |
240 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
258 |
259 |
260 |
261 |
262 |
263 |
266 |
267 |
268 |
271 |
272 |
273 |
274 |
280 |
281 |
282 |
283 |
287 |
288 |
289 |
290 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
305 |
306 |
307 |
308 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
321 |
322 |
323 |
324 |
--------------------------------------------------------------------------------
/buildkit-parallelization/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | app:
3 | image: app
4 | build:
5 | context: .
6 | environment:
7 | - APP_SIGN_ALGO=HmacSHA256
8 | - APP_SIGN_KEY=073b9ce8277c4d70dba2edf33e80f6a2bde0dbd7e5c9d149ccc20ba8b125129b
9 | ports:
10 | - '8080:8080'
--------------------------------------------------------------------------------
/buildkit-parallelization/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.2.0
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | # e.g. to debug Maven itself, use
32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | # ----------------------------------------------------------------------------
35 |
36 | if [ -z "$MAVEN_SKIP_RC" ] ; then
37 |
38 | if [ -f /usr/local/etc/mavenrc ] ; then
39 | . /usr/local/etc/mavenrc
40 | fi
41 |
42 | if [ -f /etc/mavenrc ] ; then
43 | . /etc/mavenrc
44 | fi
45 |
46 | if [ -f "$HOME/.mavenrc" ] ; then
47 | . "$HOME/.mavenrc"
48 | fi
49 |
50 | fi
51 |
52 | # OS specific support. $var _must_ be set to either true or false.
53 | cygwin=false;
54 | darwin=false;
55 | mingw=false
56 | case "$(uname)" in
57 | CYGWIN*) cygwin=true ;;
58 | MINGW*) mingw=true;;
59 | Darwin*) darwin=true
60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
62 | if [ -z "$JAVA_HOME" ]; then
63 | if [ -x "/usr/libexec/java_home" ]; then
64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
65 | else
66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
67 | fi
68 | fi
69 | ;;
70 | esac
71 |
72 | if [ -z "$JAVA_HOME" ] ; then
73 | if [ -r /etc/gentoo-release ] ; then
74 | JAVA_HOME=$(java-config --jre-home)
75 | fi
76 | fi
77 |
78 | # For Cygwin, ensure paths are in UNIX format before anything is touched
79 | if $cygwin ; then
80 | [ -n "$JAVA_HOME" ] &&
81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
82 | [ -n "$CLASSPATH" ] &&
83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
84 | fi
85 |
86 | # For Mingw, ensure paths are in UNIX format before anything is touched
87 | if $mingw ; then
88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
90 | fi
91 |
92 | if [ -z "$JAVA_HOME" ]; then
93 | javaExecutable="$(which javac)"
94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
95 | # readlink(1) is not available as standard on Solaris 10.
96 | readLink=$(which readlink)
97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
98 | if $darwin ; then
99 | javaHome="$(dirname "\"$javaExecutable\"")"
100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
101 | else
102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")"
103 | fi
104 | javaHome="$(dirname "\"$javaExecutable\"")"
105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin')
106 | JAVA_HOME="$javaHome"
107 | export JAVA_HOME
108 | fi
109 | fi
110 | fi
111 |
112 | if [ -z "$JAVACMD" ] ; then
113 | if [ -n "$JAVA_HOME" ] ; then
114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
115 | # IBM's JDK on AIX uses strange locations for the executables
116 | JAVACMD="$JAVA_HOME/jre/sh/java"
117 | else
118 | JAVACMD="$JAVA_HOME/bin/java"
119 | fi
120 | else
121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
122 | fi
123 | fi
124 |
125 | if [ ! -x "$JAVACMD" ] ; then
126 | echo "Error: JAVA_HOME is not defined correctly." >&2
127 | echo " We cannot execute $JAVACMD" >&2
128 | exit 1
129 | fi
130 |
131 | if [ -z "$JAVA_HOME" ] ; then
132 | echo "Warning: JAVA_HOME environment variable is not set."
133 | fi
134 |
135 | # traverses directory structure from process work directory to filesystem root
136 | # first directory with .mvn subdirectory is considered project base directory
137 | find_maven_basedir() {
138 | if [ -z "$1" ]
139 | then
140 | echo "Path not specified to find_maven_basedir"
141 | return 1
142 | fi
143 |
144 | basedir="$1"
145 | wdir="$1"
146 | while [ "$wdir" != '/' ] ; do
147 | if [ -d "$wdir"/.mvn ] ; then
148 | basedir=$wdir
149 | break
150 | fi
151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
152 | if [ -d "${wdir}" ]; then
153 | wdir=$(cd "$wdir/.." || exit 1; pwd)
154 | fi
155 | # end of workaround
156 | done
157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)"
158 | }
159 |
160 | # concatenates all lines of a file
161 | concat_lines() {
162 | if [ -f "$1" ]; then
163 | # Remove \r in case we run on Windows within Git Bash
164 | # and check out the repository with auto CRLF management
165 | # enabled. Otherwise, we may read lines that are delimited with
166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
167 | # splitting rules.
168 | tr -s '\r\n' ' ' < "$1"
169 | fi
170 | }
171 |
172 | log() {
173 | if [ "$MVNW_VERBOSE" = true ]; then
174 | printf '%s\n' "$1"
175 | fi
176 | }
177 |
178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
179 | if [ -z "$BASE_DIR" ]; then
180 | exit 1;
181 | fi
182 |
183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
184 | log "$MAVEN_PROJECTBASEDIR"
185 |
186 | ##########################################################################################
187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
188 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
189 | ##########################################################################################
190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
191 | if [ -r "$wrapperJarPath" ]; then
192 | log "Found $wrapperJarPath"
193 | else
194 | log "Couldn't find $wrapperJarPath, downloading it ..."
195 |
196 | if [ -n "$MVNW_REPOURL" ]; then
197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
198 | else
199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
200 | fi
201 | while IFS="=" read -r key value; do
202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
203 | safeValue=$(echo "$value" | tr -d '\r')
204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
205 | esac
206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
207 | log "Downloading from: $wrapperUrl"
208 |
209 | if $cygwin; then
210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
211 | fi
212 |
213 | if command -v wget > /dev/null; then
214 | log "Found wget ... using wget"
215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
218 | else
219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
220 | fi
221 | elif command -v curl > /dev/null; then
222 | log "Found curl ... using curl"
223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
226 | else
227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
228 | fi
229 | else
230 | log "Falling back to using Java to download"
231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
233 | # For Cygwin, switch paths to Windows format before running javac
234 | if $cygwin; then
235 | javaSource=$(cygpath --path --windows "$javaSource")
236 | javaClass=$(cygpath --path --windows "$javaClass")
237 | fi
238 | if [ -e "$javaSource" ]; then
239 | if [ ! -e "$javaClass" ]; then
240 | log " - Compiling MavenWrapperDownloader.java ..."
241 | ("$JAVA_HOME/bin/javac" "$javaSource")
242 | fi
243 | if [ -e "$javaClass" ]; then
244 | log " - Running MavenWrapperDownloader.java ..."
245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
246 | fi
247 | fi
248 | fi
249 | fi
250 | ##########################################################################################
251 | # End of extension
252 | ##########################################################################################
253 |
254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file
255 | wrapperSha256Sum=""
256 | while IFS="=" read -r key value; do
257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
258 | esac
259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
260 | if [ -n "$wrapperSha256Sum" ]; then
261 | wrapperSha256Result=false
262 | if command -v sha256sum > /dev/null; then
263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
264 | wrapperSha256Result=true
265 | fi
266 | elif command -v shasum > /dev/null; then
267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
268 | wrapperSha256Result=true
269 | fi
270 | else
271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
273 | exit 1
274 | fi
275 | if [ $wrapperSha256Result = false ]; then
276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
279 | exit 1
280 | fi
281 | fi
282 |
283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
284 |
285 | # For Cygwin, switch paths to Windows format before running java
286 | if $cygwin; then
287 | [ -n "$JAVA_HOME" ] &&
288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
289 | [ -n "$CLASSPATH" ] &&
290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
291 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
293 | fi
294 |
295 | # Provide a "standardized" way to retrieve the CLI args that will
296 | # work with both Windows and non-Windows executions.
297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
298 | export MAVEN_CMD_LINE_ARGS
299 |
300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
301 |
302 | # shellcheck disable=SC2086 # safe args
303 | exec "$JAVACMD" \
304 | $MAVEN_OPTS \
305 | $MAVEN_DEBUG_OPTS \
306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
309 |
--------------------------------------------------------------------------------
/buildkit-parallelization/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
30 | @REM e.g. to debug Maven itself, use
31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
33 | @REM ----------------------------------------------------------------------------
34 |
35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
36 | @echo off
37 | @REM set title of command window
38 | title %0
39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
41 |
42 | @REM set %HOME% to equivalent of $HOME
43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
44 |
45 | @REM Execute a user defined script before this one
46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
50 | :skipRcPre
51 |
52 | @setlocal
53 |
54 | set ERROR_CODE=0
55 |
56 | @REM To isolate internal variables from possible post scripts, we use another setlocal
57 | @setlocal
58 |
59 | @REM ==== START VALIDATION ====
60 | if not "%JAVA_HOME%" == "" goto OkJHome
61 |
62 | echo.
63 | echo Error: JAVA_HOME not found in your environment. >&2
64 | echo Please set the JAVA_HOME variable in your environment to match the >&2
65 | echo location of your Java installation. >&2
66 | echo.
67 | goto error
68 |
69 | :OkJHome
70 | if exist "%JAVA_HOME%\bin\java.exe" goto init
71 |
72 | echo.
73 | echo Error: JAVA_HOME is set to an invalid directory. >&2
74 | echo JAVA_HOME = "%JAVA_HOME%" >&2
75 | echo Please set the JAVA_HOME variable in your environment to match the >&2
76 | echo location of your Java installation. >&2
77 | echo.
78 | goto error
79 |
80 | @REM ==== END VALIDATION ====
81 |
82 | :init
83 |
84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
85 | @REM Fallback to current working directory if not found.
86 |
87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
89 |
90 | set EXEC_DIR=%CD%
91 | set WDIR=%EXEC_DIR%
92 | :findBaseDir
93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
94 | cd ..
95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
96 | set WDIR=%CD%
97 | goto findBaseDir
98 |
99 | :baseDirFound
100 | set MAVEN_PROJECTBASEDIR=%WDIR%
101 | cd "%EXEC_DIR%"
102 | goto endDetectBaseDir
103 |
104 | :baseDirNotFound
105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
106 | cd "%EXEC_DIR%"
107 |
108 | :endDetectBaseDir
109 |
110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
111 |
112 | @setlocal EnableExtensions EnableDelayedExpansion
113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
115 |
116 | :endReadAdditionalConfig
117 |
118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
123 |
124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | if "%MVNW_VERBOSE%" == "true" (
132 | echo Found %WRAPPER_JAR%
133 | )
134 | ) else (
135 | if not "%MVNW_REPOURL%" == "" (
136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
137 | )
138 | if "%MVNW_VERBOSE%" == "true" (
139 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
140 | echo Downloading from: %WRAPPER_URL%
141 | )
142 |
143 | powershell -Command "&{"^
144 | "$webclient = new-object System.Net.WebClient;"^
145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
147 | "}"^
148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
149 | "}"
150 | if "%MVNW_VERBOSE%" == "true" (
151 | echo Finished downloading %WRAPPER_JAR%
152 | )
153 | )
154 | @REM End of extension
155 |
156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
157 | SET WRAPPER_SHA_256_SUM=""
158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
160 | )
161 | IF NOT %WRAPPER_SHA_256_SUM%=="" (
162 | powershell -Command "&{"^
163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
168 | " exit 1;"^
169 | "}"^
170 | "}"
171 | if ERRORLEVEL 1 goto error
172 | )
173 |
174 | @REM Provide a "standardized" way to retrieve the CLI args that will
175 | @REM work with both Windows and non-Windows executions.
176 | set MAVEN_CMD_LINE_ARGS=%*
177 |
178 | %MAVEN_JAVA_EXE% ^
179 | %JVM_CONFIG_MAVEN_PROPS% ^
180 | %MAVEN_OPTS% ^
181 | %MAVEN_DEBUG_OPTS% ^
182 | -classpath %WRAPPER_JAR% ^
183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
185 | if ERRORLEVEL 1 goto error
186 | goto end
187 |
188 | :error
189 | set ERROR_CODE=1
190 |
191 | :end
192 | @endlocal & set ERROR_CODE=%ERROR_CODE%
193 |
194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
198 | :skipRcPost
199 |
200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
202 |
203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
204 |
205 | cmd /C exit /B %ERROR_CODE%
206 |
--------------------------------------------------------------------------------
/buildkit-parallelization/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | sign-service
9 | 1.0.0
10 |
11 |
12 | 17
13 | 17
14 | UTF-8
15 |
16 | 1.18.28
17 | 1.4.11
18 | 1.16.0
19 |
20 | 11.0.6
21 | 5.10.0
22 |
23 | 3.1.2
24 | 3.6.0
25 | 3.3.0
26 | 4.7.3.6
27 | 8.4.0
28 |
29 | checkstyle.xml
30 |
31 |
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | ${lombok.version}
37 | provided
38 |
39 |
40 | ch.qos.logback
41 | logback-classic
42 | ${logback-classic.version}
43 |
44 |
45 | commons-codec
46 | commons-codec
47 | ${commons-codec.version}
48 |
49 |
50 | org.eclipse.jetty
51 | jetty-servlet
52 | ${jetty-servlet.version}
53 |
54 |
55 | org.junit.jupiter
56 | junit-jupiter
57 | ${junit-jupiter.version}
58 | test
59 |
60 |
61 |
62 |
63 |
64 | app
65 |
66 |
67 | maven-surefire-plugin
68 | ${maven-surefire-plugin.version}
69 |
70 |
71 |
72 | maven-assembly-plugin
73 | ${maven-assembly-plugin.version}
74 |
75 |
76 |
77 | org.example.Main
78 |
79 |
80 |
81 | jar-with-dependencies
82 |
83 | false
84 |
85 |
86 |
87 |
88 | single
89 |
90 | package
91 |
92 |
93 |
94 |
95 |
96 | org.apache.maven.plugins
97 | maven-checkstyle-plugin
98 | ${maven-checkstyle-plugin.version}
99 |
100 |
101 |
102 | check
103 |
104 |
105 |
106 |
107 |
108 |
109 | com.github.spotbugs
110 | spotbugs-maven-plugin
111 | ${spotbugs-maven-plugin.version}
112 |
113 |
114 |
115 | check
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | org.owasp
124 | dependency-check-maven
125 | ${dependency-check-maven.version}
126 |
127 | true
128 |
129 |
130 |
131 |
132 |
133 | check
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/main/java/org/example/Main.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import lombok.SneakyThrows;
4 | import org.eclipse.jetty.server.Server;
5 | import org.eclipse.jetty.servlet.ServletHandler;
6 | import org.example.servlet.SignServlet;
7 |
8 | public class Main {
9 | @SneakyThrows
10 | public static void main(String[] args) {
11 | final Server server = new Server(8080);
12 |
13 | final ServletHandler handler = new ServletHandler();
14 | // no initialization for servlet/service
15 | handler.addServletWithMapping(SignServlet.class, "/");
16 | server.setHandler(handler);
17 |
18 | server.start();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/main/java/org/example/exception/AppException.java:
--------------------------------------------------------------------------------
1 | package org.example.exception;
2 |
3 | public class AppException extends RuntimeException {
4 | public AppException() {
5 | }
6 |
7 | public AppException(final String message) {
8 | super(message);
9 | }
10 |
11 | public AppException(final String message, final Throwable cause) {
12 | super(message, cause);
13 | }
14 |
15 | public AppException(final Throwable cause) {
16 | super(cause);
17 | }
18 |
19 | public AppException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
20 | super(message, cause, enableSuppression, writableStackTrace);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/main/java/org/example/exception/ConfigAppException.java:
--------------------------------------------------------------------------------
1 | package org.example.exception;
2 |
3 | public class ConfigAppException extends AppException {
4 | public ConfigAppException() {
5 | }
6 |
7 | public ConfigAppException(final String message) {
8 | super(message);
9 | }
10 |
11 | public ConfigAppException(final String message, final Throwable cause) {
12 | super(message, cause);
13 | }
14 |
15 | public ConfigAppException(final Throwable cause) {
16 | super(cause);
17 | }
18 |
19 | public ConfigAppException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
20 | super(message, cause, enableSuppression, writableStackTrace);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/main/java/org/example/service/SignService.java:
--------------------------------------------------------------------------------
1 | package org.example.service;
2 |
3 | import lombok.SneakyThrows;
4 | import org.apache.commons.codec.binary.Hex;
5 | import org.example.exception.ConfigAppException;
6 |
7 | import javax.crypto.Mac;
8 | import javax.crypto.spec.SecretKeySpec;
9 | import java.util.Arrays;
10 | import java.util.Optional;
11 |
12 | public class SignService {
13 | public static final String APP_SIGN_ALGO = "APP_SIGN_ALGO";
14 | public static final String APP_SIGN_KEY = "APP_SIGN_KEY";
15 | private final String algo;
16 | private final byte[] key;
17 |
18 | public SignService() {
19 | algo = Optional.ofNullable(System.getenv(APP_SIGN_ALGO))
20 | .orElseThrow(() -> new ConfigAppException("No algorithm provided"));
21 |
22 | key = Optional.ofNullable(System.getenv(APP_SIGN_KEY))
23 | .map(this::hexDecode)
24 | .orElseThrow(() -> new ConfigAppException("No key provided"));
25 | }
26 |
27 | public SignService(final String algo, final byte[] key) {
28 | this.algo = algo;
29 | this.key = Arrays.copyOf(key, key.length);
30 | }
31 |
32 | @SneakyThrows
33 | public String sign(final byte[] data) {
34 | final SecretKeySpec secretKeySpec = new SecretKeySpec(key, algo);
35 | final Mac mac = Mac.getInstance(algo);
36 | mac.init(secretKeySpec);
37 | mac.update(data);
38 | final byte[] signature = mac.doFinal();
39 | return Hex.encodeHexString(signature);
40 | }
41 |
42 | @SneakyThrows
43 | private byte[] hexDecode(final String string) {
44 | return Hex.decodeHex(string);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/main/java/org/example/servlet/SignServlet.java:
--------------------------------------------------------------------------------
1 | package org.example.servlet;
2 |
3 | import jakarta.servlet.ServletException;
4 | import jakarta.servlet.annotation.WebServlet;
5 | import jakarta.servlet.http.HttpServlet;
6 | import jakarta.servlet.http.HttpServletRequest;
7 | import jakarta.servlet.http.HttpServletResponse;
8 | import org.example.service.SignService;
9 |
10 | import java.io.IOException;
11 |
12 | public class SignServlet extends HttpServlet {
13 | private static final long serialVersionUID = -7589549632947183818L;
14 | private final transient SignService service = new SignService();
15 |
16 | @Override
17 | protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
18 | final byte[] bytes = req.getInputStream().readAllBytes();
19 | final String sign = service.sign(bytes);
20 | resp.getWriter().write(sign);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/buildkit-parallelization/src/test/java/org/example/service/SignServiceTest.java:
--------------------------------------------------------------------------------
1 | package org.example.service;
2 |
3 | import lombok.SneakyThrows;
4 | import org.apache.commons.codec.binary.Hex;
5 | import org.junit.jupiter.api.Assertions;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import java.nio.charset.StandardCharsets;
9 |
10 | class SignServiceTest {
11 | @SneakyThrows
12 | @Test
13 | void shouldSign() {
14 | final String algo = "HmacSHA256";
15 | final String key = "073b9ce8277c4d70dba2edf33e80f6a2bde0dbd7e5c9d149ccc20ba8b125129b";
16 | final byte[] data = "secret".getBytes(StandardCharsets.UTF_8);
17 | final String expected = "b4a5151c4d6939f8df7febe32ac94c3ce0f61112cbe1d8df0e51d7f2c4d9618b";
18 |
19 | final SignService service = new SignService(algo, Hex.decodeHex(key));
20 |
21 | final String actual = service.sign(data);
22 |
23 | Assertions.assertEquals(expected, actual);
24 | }
25 | }
--------------------------------------------------------------------------------
/db-for-testers/.gitignore:
--------------------------------------------------------------------------------
1 | target/
--------------------------------------------------------------------------------
/db-for-testers/README.md:
--------------------------------------------------------------------------------
1 | Теги: `Docker` `PostgreSQL`
2 |
3 | # DB for Testers
4 |
5 | ## Задача
6 |
7 | Наши тестировщики прониклись идеями контейнеризации и активно начинают использовать Docker в своих тестах (ручных, автоматизированных и т.д.)
8 |
9 | Им очень нравится идея, что они могут просто взять контейнер PostgreSQL, закинуть туда БД и запускать тестируемые приложения с нужными данными
10 |
11 | Но вот проблема, они работают следующим образом:
12 | 1. Поднимают чистый контейнер
13 | 2. Инициализируют его данными (закидывая в `docker-entrypoint-initdb.d` конечно же)
14 | 3. Запускают серию тестов
15 |
16 | После этого им снова нужен чистый контейнер с теми же данными (т.е. приходится повторять шаги 1-2)
17 |
18 | Иногда эти шаги достаточно длительные — см. пример в `docker-entrypoint-initdb.d`
19 |
20 | Поэтому они очень просят сделать им образ уже сразу с инициализированным PostgreSQL, где в базе уже будут готовые данные (мы, конечно, понимаем, что это можно сделать и через `docker commit`, но давайте попробуем обойтись `docker build`, раз уж наш Навыкум про сборку контейнеров)
21 |
22 | Название базы и её владельца (и пароль, конечно же) нужно передавать через аргументы при сборке образа
23 |
24 |
25 | Спойлеры
26 |
27 | Для решения этой задачи настоятельно рекомендуем ознакомиться с docker-library/postgres#496
28 |
29 |
30 | ### Что нужно сделать
31 |
32 | Соберите образ с инициализированной БД, используя данные из `docker-entrypoint-initdb.d` (для этого, конечно же, нужно написать `Dockerfile`)
33 |
34 | За основу возьмите образ 15.4-alpine3.18
35 |
36 | При сборке через аргументы передавайте следующие данные:
37 | 1. Имя БД — `db` (`ARG DBNAME`)
38 | 2. Имя пользователя — `app` (`ARG DBUSER`)
39 | 3. Пароль пользователя — `pass` (`ARG DBPASS`)
40 |
41 | Никаких доп.настроек не нужно
42 |
43 | В собранном образе должен запускаться PostgreSQL на порту 5432 с инициализированной базой
44 |
45 | ## Реализация
46 |
47 | В качестве реализации CI/CD пайплайна и Docker Registry можно использовать любые, например (из облачных и бесплатных), [GitHub Actions](https://docs.github.com/en/actions) и [GitHub Packages](https://docs.github.com/packages)
48 |
--------------------------------------------------------------------------------
/db-for-testers/docker-entrypoint-initdb.d/data.sql:
--------------------------------------------------------------------------------
1 | -- just emulate hard work
2 | SELECT clock_timestamp(), pg_sleep(30), clock_timestamp();
3 |
4 | INSERT INTO "users"("login", "password", "roles")
5 | VALUES
6 | ('root', '{argon2}$argon2id$v=19$m=4096,t=3,p=1$VD2gbE9s9SvxSU3QnmeO9w$hosiwDgCWLdyZ6xrysnDg9fDE38frM65jxOj58ZkCXs', '{ROOT}'),
7 | ('admin', '{argon2}$argon2id$v=19$m=4096,t=3,p=1$VD2gbE9s9SvxSU3QnmeO9w$hosiwDgCWLdyZ6xrysnDg9fDE38frM65jxOj58ZkCXs', '{ADMIN}'),
8 | ('user', '{argon2}$argon2id$v=19$m=4096,t=3,p=1$VD2gbE9s9SvxSU3QnmeO9w$hosiwDgCWLdyZ6xrysnDg9fDE38frM65jxOj58ZkCXs', DEFAULT)
9 | RETURNING *
10 | ;
--------------------------------------------------------------------------------
/db-for-testers/docker-entrypoint-initdb.d/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "users"
2 | (
3 | "id" BIGSERIAL PRIMARY KEY,
4 | "login" TEXT NOT NULL UNIQUE,
5 | "password" TEXT NOT NULL,
6 | "roles" TEXT[] NOT NULL DEFAULT '{USER}',
7 | "created" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
8 | );
--------------------------------------------------------------------------------
/graalvm-native-image-static/.gitignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | dev-*.yml
3 | HELP.md
4 | target/
5 | !.mvn/wrapper/maven-wrapper.jar
6 | !**/src/main/**/target/
7 | !**/src/test/**/target/
8 |
9 | ### STS ###
10 | .apt_generated
11 | .classpath
12 | .factorypath
13 | .project
14 | .settings
15 | .springBeans
16 | .sts4-cache
17 |
18 | ### IntelliJ IDEA ###
19 | .idea
20 | *.iws
21 | *.iml
22 | *.ipr
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
--------------------------------------------------------------------------------
/graalvm-native-image-static/README.md:
--------------------------------------------------------------------------------
1 | Теги: `Docker` `Java` `Maven` `GraalVM` `Native`
2 |
3 | # GraalVM Native Image (Static)
4 |
5 | ## Задача
6 |
7 | Есть проект на чистой Java 17, система сборки — Maven, разработчики хотят попробовать [GraalVM Native Image](https://www.graalvm.org/jdk17/reference-manual/native-image/)
8 |
9 | Для Maven есть [Maven Native Plugin](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html), который и позволит скомпилировать приложение в бинарный исполняемый файл формата ELF
10 |
11 | Разработчики хотят сделать так, чтобы выбор между сборкой "обычного приложения" и Native Image осуществлялся через [профили Maven](https://maven.apache.org/guides/introduction/introduction-to-profiles.html), определяемые в самом проекте
12 |
13 | По умолчанию, активным должен быть профиль, который собирает "обычное приложение" (не Native Image)
14 |
15 | ### Сборка
16 |
17 | Сборка проходит стандартным образом, через `mvn package`, никаких внешних зависимостей у проекта (в виде подключаемых при исполнении библиотек нет)
18 |
19 | ### API
20 |
21 | Сервер запускается на порту, указанном через параметр `port` или переменную окружения `PORT` и реагирует на команду `UUID`, выдавая в ответ случайный [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)
22 |
23 | Как проверить:
24 | 1. Подключаемся с помощью `nc` (`netcat`) по нужному порту
25 | 2. Вводим `UUID⏎`, (где `⏎` — Enter для отправки данных)
26 | 3. Получаем в ответ: `b022e6b9-957a-4f22-b519-2fb57ca76caf` (пример)
27 |
28 |
29 | Спойлеры: пример вызова nc для тестирования
30 |
31 | ```shell
32 | $ nc -u localhost 9999
33 | UUID
34 | b022e6b9-957a-4f22-b519-2fb57ca76caf
35 | ```
36 |
37 |
38 |
39 | ### Что нужно сделать
40 |
41 | 1. Собрать всё с помощью Maven и Native Image (параллелить ничего не нужно, т.к. в приложении нет авто-тестов, проверок стиля кода и т.д.)
42 | 2. Упаковать всё в [`Scratch`](https://hub.docker.com/_/scratch)
43 | 3. Запускать приложение не от root
44 |
45 | ## Реализация
46 |
47 | В качестве реализации CI/CD пайплайна и Docker Registry можно использовать любые, например (из облачных и бесплатных), [GitHub Actions](https://docs.github.com/en/actions) и [GitHub Packages](https://docs.github.com/packages)
48 |
49 | ## Полезные ссылки
50 |
51 | 1. [GraalVM Native Image](https://www.graalvm.org/jdk17/reference-manual/native-image/)
52 | 2. [Netcat MAN](https://linux.die.net/man/1/nc)
53 |
--------------------------------------------------------------------------------
/graalvm-native-image-static/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | native
9 | 1.0.0
10 |
11 |
12 | 17
13 | 17
14 | UTF-8
15 | 1.18.30
16 |
17 |
18 |
19 |
20 | org.projectlombok
21 | lombok
22 | ${lombok.version}
23 | provided
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/graalvm-native-image-static/src/main/java/org/example/Main.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.example.server.Server;
4 |
5 | import java.io.IOException;
6 | import java.util.Optional;
7 |
8 | public class Main {
9 | public static void main(String[] args) throws IOException {
10 | final Server server = new Server();
11 | final String port = Optional.ofNullable(System.getProperty("port"))
12 | .orElse(System.getenv("PORT"))
13 | ;
14 | server.serve(Integer.parseInt(port));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/graalvm-native-image-static/src/main/java/org/example/server/Server.java:
--------------------------------------------------------------------------------
1 | package org.example.server;
2 |
3 | import lombok.extern.java.Log;
4 |
5 | import java.io.IOException;
6 | import java.net.DatagramPacket;
7 | import java.net.DatagramSocket;
8 | import java.nio.charset.StandardCharsets;
9 | import java.util.UUID;
10 | import java.util.logging.Level;
11 |
12 | @Log
13 | public class Server {
14 | public void serve(final int port) throws IOException {
15 | try (
16 | final DatagramSocket datagramSocket = new DatagramSocket(port);
17 | ) {
18 | log.log(Level.INFO, "server created, listen on: {0}", port);
19 |
20 | while (true) {
21 | final byte[] buffer = new byte[1024];
22 | final DatagramPacket in = new DatagramPacket(buffer, buffer.length);
23 | datagramSocket.receive(in);
24 | log.log(Level.INFO, "received packet from {0}:{1}", new Object[]{in.getAddress(), in.getPort()});
25 | final String incomingMessage = new String(in.getData(), in.getOffset(), in.getLength(), StandardCharsets.UTF_8).trim();
26 | log.log(Level.FINE, "message: {0}", incomingMessage);
27 |
28 | switch (incomingMessage) {
29 | case "UUID": {
30 | final byte[] replyMessage = String.format("%s%n", UUID.randomUUID()).getBytes(StandardCharsets.UTF_8);
31 | final DatagramPacket out = new DatagramPacket(replyMessage, replyMessage.length, in.getAddress(), in.getPort());
32 | datagramSocket.send(out);
33 | break;
34 | }
35 | default: {
36 | final byte[] replyMessage = String.format("Invalid Command: %s%n", incomingMessage).getBytes(StandardCharsets.UTF_8);
37 | final DatagramPacket out = new DatagramPacket(replyMessage, replyMessage.length, in.getAddress(), in.getPort());
38 | datagramSocket.send(out);
39 | break;
40 | }
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/gradle-application/.gitignore:
--------------------------------------------------------------------------------
1 | /tomcat*
2 | .gradle
3 | build/
4 | !gradle/wrapper/gradle-wrapper.jar
5 | !**/src/main/**/build/
6 | !**/src/test/**/build/
7 |
8 | ### IntelliJ IDEA ###
9 | .idea/
10 | *.iws
11 | *.iml
12 | *.ipr
13 | out/
14 | !**/src/main/**/out/
15 | !**/src/test/**/out/
16 |
17 | ### Eclipse ###
18 | .apt_generated
19 | .classpath
20 | .factorypath
21 | .project
22 | .settings
23 | .springBeans
24 | .sts4-cache
25 | bin/
26 | !**/src/main/**/bin/
27 | !**/src/test/**/bin/
28 |
29 | ### NetBeans ###
30 | /nbproject/private/
31 | /nbbuild/
32 | /dist/
33 | /nbdist/
34 | /.nb-gradle/
35 |
36 | ### VS Code ###
37 | .vscode/
38 |
39 | ### Mac OS ###
40 | .DS_Store
--------------------------------------------------------------------------------
/gradle-application/README.md:
--------------------------------------------------------------------------------
1 | Теги: `Docker` `Java` `Gradle`
2 |
3 | # Gradle Application
4 |
5 | ## Задача
6 |
7 | Разработчики проекта совсем не дружат с Docker, контейнерами и "всей этой вашей новомодной \[подставьте наименование по желанию\]", поэтому нам выпала задача упаковать это всё в образ
8 |
9 | Что имеем: проект на Java 17 с системой сборки Gradle
10 |
11 | Используемый порт: `9999`
12 |
13 | Команда для тестирования:
14 | ```shell
15 | curl -X GET http://localhost:9999
16 | ```
17 |
18 | Ожидаемый ответ:
19 | ```shell
20 | {"uuid": "рандомный UUID"}
21 | ```
22 |
23 | Также есть Healthcheck:
24 | ```shell
25 | curl -X GET http://localhost:9999/health
26 | ```
27 |
28 | Ожидаемый ответ:
29 | ```shell
30 | {"status": "UP"}
31 | ```
32 |
33 | Приоритеты по использованию базового образа:
34 | 1. Scratch
35 | 2. Distroless (не `debug`)
36 | 3. Eclipse Temurin
37 |
38 | Запуск не от `root`'а
39 |
40 |
41 | Спойлеры
42 |
43 | Вполне возможно, что первые два варианта использовать нецелесообразно по техническим соображениям: подготовьте обоснованный ответ на вопрос "почему"
44 |
45 |
46 |
47 | ## Инструкции по сборке проекта
48 |
49 | Есть плагин [`application`](https://docs.gradle.org/current/userguide/application_plugin.html), который умеет собирать дистрибутив проекта
50 |
51 | Делается это с помощью следующей команды:
52 |
53 | ```shell
54 | ./gradlew assemble
55 | ```
56 |
57 | Файл `gradle-application-1.0.0.tar` и будет содержать дистрибутив для развёртывания
58 |
59 | ## Реализация
60 |
61 | В качестве реализации CI/CD пайплайна и Docker Registry можно использовать любые, например (из облачных и бесплатных), [GitHub Actions](https://docs.github.com/en/actions) и [GitHub Packages](https://docs.github.com/packages)
62 |
63 |
--------------------------------------------------------------------------------
/gradle-application/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id 'application'
4 | }
5 |
6 | group = 'org.example'
7 | version = '1.0.0'
8 |
9 | repositories {
10 | mavenCentral()
11 | }
12 |
13 | dependencies {
14 | // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
15 | implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.15'
16 | // https://mvnrepository.com/artifact/com.google.code.gson/gson
17 | implementation 'com.google.code.gson:gson:2.10.1'
18 | }
19 |
--------------------------------------------------------------------------------
/gradle-application/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slurmio/devopscases/09b9f14317068252642b162b73b30279e20a2925/gradle-application/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle-application/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Oct 24 09:08:01 MSK 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradle-application/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradle-application/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/gradle-application/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'gradle-application'
2 |
3 |
--------------------------------------------------------------------------------
/gradle-application/src/main/java/org/example/Main.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import com.google.gson.Gson;
4 | import org.apache.catalina.Context;
5 | import org.apache.catalina.LifecycleException;
6 | import org.apache.catalina.connector.Connector;
7 | import org.apache.catalina.startup.Tomcat;
8 | import org.example.serlvet.HealthServlet;
9 | import org.example.serlvet.MainServlet;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 | import java.nio.file.Files;
14 | import java.nio.file.Path;
15 |
16 | public class Main {
17 | public static void main(String[] args) throws IOException, LifecycleException {
18 | final int port = 9999;
19 | final Tomcat tomcat = new Tomcat();
20 | tomcat.setPort(port);
21 |
22 | final Connector connector = new Connector("HTTP/1.1");
23 | connector.setPort(port);
24 | tomcat.setConnector(connector);
25 |
26 |
27 | final Path baseDir = Files.createTempDirectory("tomcat-");
28 | tomcat.setBaseDir(baseDir.toFile().getAbsolutePath());
29 |
30 | final Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());
31 |
32 | final Gson gson = new Gson();
33 | final MainServlet mainServlet = new MainServlet(gson);
34 |
35 | Tomcat.addServlet(ctx, "main", mainServlet);
36 | ctx.addServletMappingDecoded("/*", "main");
37 |
38 | final HealthServlet healthServlet = new HealthServlet(gson);
39 | Tomcat.addServlet(ctx, "health", healthServlet);
40 | ctx.addServletMappingDecoded("/health", "health");
41 |
42 | tomcat.start();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/gradle-application/src/main/java/org/example/dto/HealthResponseDto.java:
--------------------------------------------------------------------------------
1 | package org.example.dto;
2 |
3 | public record HealthResponseDto(Status status) {
4 | public enum Status {
5 | UP, DOWN
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/gradle-application/src/main/java/org/example/dto/MainResponseDto.java:
--------------------------------------------------------------------------------
1 | package org.example.dto;
2 |
3 | import java.util.UUID;
4 |
5 | public record MainResponseDto(UUID uuid) {
6 | }
7 |
--------------------------------------------------------------------------------
/gradle-application/src/main/java/org/example/serlvet/HealthServlet.java:
--------------------------------------------------------------------------------
1 | package org.example.serlvet;
2 |
3 | import com.google.gson.Gson;
4 | import jakarta.servlet.http.HttpServlet;
5 | import jakarta.servlet.http.HttpServletRequest;
6 | import jakarta.servlet.http.HttpServletResponse;
7 | import org.example.dto.HealthResponseDto;
8 |
9 | import java.io.IOException;
10 |
11 | public class HealthServlet extends HttpServlet {
12 | private final transient Gson gson;
13 |
14 | public HealthServlet(final Gson gson) {
15 | this.gson = gson;
16 | }
17 |
18 | @Override
19 | protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
20 | final HealthResponseDto dto = new HealthResponseDto(HealthResponseDto.Status.UP);
21 | resp.setHeader("Content-Type", "application/json");
22 | this.gson.toJson(dto, resp.getWriter());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/gradle-application/src/main/java/org/example/serlvet/MainServlet.java:
--------------------------------------------------------------------------------
1 | package org.example.serlvet;
2 |
3 | import com.google.gson.Gson;
4 | import jakarta.servlet.ServletException;
5 | import jakarta.servlet.http.HttpServlet;
6 | import jakarta.servlet.http.HttpServletRequest;
7 | import jakarta.servlet.http.HttpServletResponse;
8 | import org.example.dto.MainResponseDto;
9 |
10 | import java.io.IOException;
11 | import java.time.Instant;
12 | import java.util.UUID;
13 |
14 | public class MainServlet extends HttpServlet {
15 | private final transient Gson gson;
16 |
17 | public MainServlet(final Gson gson) {
18 | this.gson = gson;
19 | }
20 |
21 | @Override
22 | protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
23 | final MainResponseDto dto = new MainResponseDto(UUID.randomUUID());
24 | resp.setHeader("Content-Type", "application/json");
25 | this.gson.toJson(dto, resp.getWriter());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------