├── db-for-testers ├── .gitignore ├── docker-entrypoint-initdb.d │ ├── schema.sql │ └── data.sql └── README.md ├── .gitignore ├── gradle-application ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ └── main │ │ └── java │ │ └── org │ │ └── example │ │ ├── dto │ │ ├── MainResponseDto.java │ │ └── HealthResponseDto.java │ │ ├── serlvet │ │ ├── HealthServlet.java │ │ └── MainServlet.java │ │ └── Main.java ├── build.gradle ├── .gitignore ├── README.md ├── gradlew.bat └── gradlew ├── angular-storybook ├── storybook.png ├── projects │ └── components │ │ ├── .storybook │ │ ├── typings.d.ts │ │ ├── tsconfig.json │ │ ├── preview.ts │ │ └── main.ts │ │ ├── src │ │ ├── stories │ │ │ ├── .eslintrc.json │ │ │ └── Introduction.mdx │ │ ├── lib │ │ │ ├── components.service.ts │ │ │ ├── counter │ │ │ │ ├── counter.component.html │ │ │ │ ├── counter.component.css │ │ │ │ ├── counter.component.spec.ts │ │ │ │ ├── counter.component.ts │ │ │ │ └── counter.component.stories.ts │ │ │ ├── components.component.ts │ │ │ ├── components.module.ts │ │ │ ├── components.service.spec.ts │ │ │ └── components.component.spec.ts │ │ └── public-api.ts │ │ ├── ng-package.json │ │ ├── package.json │ │ ├── tsconfig.lib.prod.json │ │ ├── tsconfig.spec.json │ │ ├── tsconfig.lib.json │ │ ├── .eslintrc.json │ │ ├── README.md │ │ └── documentation.json ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── .eslintrc.json ├── package.json ├── angular.json └── README.md ├── graalvm-native-image-static ├── src │ └── main │ │ └── java │ │ └── org │ │ └── example │ │ ├── Main.java │ │ └── server │ │ └── Server.java ├── .gitignore ├── pom.xml └── README.md └── README.md /db-for-testers/.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | NOTES.md 3 | Dockerfile.done 4 | docker-compose.done.yml 5 | -------------------------------------------------------------------------------- /gradle-application/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'gradle-application' 2 | 3 | -------------------------------------------------------------------------------- /angular-storybook/storybook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slurmio/devopscases/HEAD/angular-storybook/storybook.png -------------------------------------------------------------------------------- /angular-storybook/projects/components/.storybook/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.md' { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /gradle-application/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slurmio/devopscases/HEAD/gradle-application/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/lib/components.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class ComponentsService { 7 | } 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/Introduction.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/blocks'; 2 | 3 | 4 | 5 | # Welcome to Storybook 6 | 7 | Этот Storybook демонстрирует использование нашей библиотеки компонентов -------------------------------------------------------------------------------- /angular-storybook/projects/components/src/lib/counter/counter.component.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ); -------------------------------------------------------------------------------- /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.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/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/.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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ; -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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.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/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/.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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | > ![Storybook](storybook.png) 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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{{value}}\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 | } --------------------------------------------------------------------------------