From 19553a3e988a977fd9c65b04cf591f0b66df755b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3berta=20Andersen?= Date: Fri, 20 Dec 2024 10:34:15 +0000 Subject: [PATCH] Uni pr (#17308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: cd test app * fix: rename app * fix: name * fix: added dummy queries and codegen target * fix: eslint * fix: style * fix: import query * fix: import shared type * fix: affect shared/types * fix: add docker-next target * fix: nx project and infra * chore: eslint * chore: eslint * chore: remove jsx-a11y * fix: ignore css * chore: nx format:write update dirty files * Chore: adding unicorn to uber chart * fix: support values file per service * fix: cleanup * fix: added values per service * fix: only a single service please * fix: set service toplevel * fix: format * fix: use yaml not js-yaml * fix: remove release-tag arg * fix: console message * chore: remove helm values refactor * chore: remove helm values * chore: yarn charts * chore: charts update dirty files * chore: nx format:write update dirty files * chore: charts update dirty files * fix: make unaffected * Unicorn pipe (#17094) * chore: Adding pipeline for unicorns * unicorn pipe * unicorn pipe * unicorn pipe * unicorn pipe * pipetest * pipetest * pipetest * chore: nx format:write update dirty files * Update .github/workflows/unicorns.yml Co-authored-by: Jón Levy * suggestions * chore: nx format:write update dirty files * unicorn pipe * suggestions --------- Co-authored-by: andes-it Co-authored-by: Jón Levy * chore: charts update dirty files * chore: Adding pipeline to detect unicorns (#17165) * chore: Adding tests if pipeline should run on unicorns * chore: nx format:write update dirty files * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: find release branch * chore: nx format:write update dirty files * chore: find release branch * chore: find release branch * cleanup * branch sync * mergequeue * chore: testing pipeline * chore: nx format:write update dirty files * ignore * ignore * ignore * testing pipeline * testing pipeline * testing pipeline --------- Co-authored-by: andes-it * chore: nx format:write update dirty files * chore: testing mergequeue * Adding mergequeue ci * chore: testing mergequeue * chore: testing mergequeue * Uni pr (#17280) * chore: testing mergequeue * Adding mergequeue ci * chore: testing mergequeue * chore: testing mergequeue * chore: testing mergequeue * chore: testing mergequeue * chore: nx format:write update dirty files * chore: nx format:write update dirty files * chore: testing mergequeue * chore: testing mergequeue * Uni pr (#17280) * chore: testing mergequeue * Adding mergequeue ci * chore: testing mergequeue * chore: testing mergequeue * chore: testing mergequeue --------- Co-authored-by: Jón Levy Co-authored-by: andes-it --- .github/workflows/config-values.yaml | 5 + .github/workflows/external-checks.yml | 4 +- .github/workflows/merge-queue-ci.yml | 19 + .github/workflows/pullrequest-close.yml | 3 + .github/workflows/pullrequest-lint.yml | 3 + .github/workflows/pullrequest.yml | 5 +- .github/workflows/push.yml | 5 + .github/workflows/revert-pr.yaml | 4 + .github/workflows/unicorns.yml | 106 +++++ .gitignore | 1 + apps/unicorn-app/.eslintrc.json | 55 +++ apps/unicorn-app/codegen.yml | 24 + apps/unicorn-app/index.d.ts | 6 + apps/unicorn-app/infra/infra.ts | 34 ++ apps/unicorn-app/jest.config.ts | 11 + apps/unicorn-app/next-env.d.ts | 5 + apps/unicorn-app/next.config.js | 22 + apps/unicorn-app/project.json | 101 +++++ apps/unicorn-app/public/.gitkeep | 0 apps/unicorn-app/public/favicon.ico | Bin 0 -> 15086 bytes apps/unicorn-app/server.ts | 6 + apps/unicorn-app/specs/index.spec.tsx | 11 + apps/unicorn-app/src/app/api/hello/route.ts | 3 + apps/unicorn-app/src/app/global.css | 409 ++++++++++++++++++ apps/unicorn-app/src/app/layout.tsx | 18 + apps/unicorn-app/src/app/page.module.css | 2 + apps/unicorn-app/src/app/page.tsx | 17 + apps/unicorn-app/tsconfig.json | 30 ++ apps/unicorn-app/tsconfig.server.json | 11 + apps/unicorn-app/tsconfig.spec.json | 21 + apps/unicorn-app/webpack.config.js | 8 + charts/islandis/values.dev.yaml | 64 +++ charts/islandis/values.prod.yaml | 64 +++ charts/islandis/values.staging.yaml | 64 +++ charts/services/unicorn-app/values.dev.yaml | 81 ++++ charts/services/unicorn-app/values.prod.yaml | 81 ++++ .../services/unicorn-app/values.staging.yaml | 81 ++++ infra/src/uber-charts/islandis.ts | 6 + scripts/ci/create-release.mjs | 35 ++ scripts/ci/get-last-release.mjs | 17 + scripts/ci/unicorn-utils.mjs | 17 + tsconfig.base.json | 1 + 42 files changed, 1458 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/merge-queue-ci.yml create mode 100644 .github/workflows/unicorns.yml create mode 100644 apps/unicorn-app/.eslintrc.json create mode 100644 apps/unicorn-app/codegen.yml create mode 100644 apps/unicorn-app/index.d.ts create mode 100644 apps/unicorn-app/infra/infra.ts create mode 100644 apps/unicorn-app/jest.config.ts create mode 100644 apps/unicorn-app/next-env.d.ts create mode 100644 apps/unicorn-app/next.config.js create mode 100644 apps/unicorn-app/project.json create mode 100644 apps/unicorn-app/public/.gitkeep create mode 100644 apps/unicorn-app/public/favicon.ico create mode 100644 apps/unicorn-app/server.ts create mode 100644 apps/unicorn-app/specs/index.spec.tsx create mode 100644 apps/unicorn-app/src/app/api/hello/route.ts create mode 100644 apps/unicorn-app/src/app/global.css create mode 100644 apps/unicorn-app/src/app/layout.tsx create mode 100644 apps/unicorn-app/src/app/page.module.css create mode 100644 apps/unicorn-app/src/app/page.tsx create mode 100644 apps/unicorn-app/tsconfig.json create mode 100644 apps/unicorn-app/tsconfig.server.json create mode 100644 apps/unicorn-app/tsconfig.spec.json create mode 100644 apps/unicorn-app/webpack.config.js create mode 100644 charts/services/unicorn-app/values.dev.yaml create mode 100644 charts/services/unicorn-app/values.prod.yaml create mode 100644 charts/services/unicorn-app/values.staging.yaml create mode 100644 scripts/ci/create-release.mjs create mode 100644 scripts/ci/get-last-release.mjs create mode 100644 scripts/ci/unicorn-utils.mjs diff --git a/.github/workflows/config-values.yaml b/.github/workflows/config-values.yaml index 883861366f8b..e349d4ca9c26 100644 --- a/.github/workflows/config-values.yaml +++ b/.github/workflows/config-values.yaml @@ -6,12 +6,17 @@ on: - 'main' - 'release/**' - 'pre-release/**' + - '!unicorn-pipe-rel3' + - '!feature/unicorn-app' paths: - 'charts/**' - 'infra/**' - '**/infra/**' workflow_dispatch: {} pull_request: + branches: + - '!uni-pr' + - '!feature/unicorn-app' paths: - 'charts/**' - 'infra/**' diff --git a/.github/workflows/external-checks.yml b/.github/workflows/external-checks.yml index 5707795d4f9b..816915e60af3 100644 --- a/.github/workflows/external-checks.yml +++ b/.github/workflows/external-checks.yml @@ -1,7 +1,9 @@ name: External checks on: - pull_request: {} + pull_request: + branches-ignore: + - 'feature/unicorn-app' workflow_dispatch: {} defaults: diff --git a/.github/workflows/merge-queue-ci.yml b/.github/workflows/merge-queue-ci.yml new file mode 100644 index 000000000000..2e96f313443e --- /dev/null +++ b/.github/workflows/merge-queue-ci.yml @@ -0,0 +1,19 @@ +name: Validate code in the merge queue + +on: + merge_group: + +jobs: + validate-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Display info + run: | + echo "Hallo Merge Queue" + pwd + tree -a -I '.git' + git status + - name: Run slow CI (emulated by a long sleep) + run: sleep 300 diff --git a/.github/workflows/pullrequest-close.yml b/.github/workflows/pullrequest-close.yml index 5d3e066f3866..76e88bbcba19 100644 --- a/.github/workflows/pullrequest-close.yml +++ b/.github/workflows/pullrequest-close.yml @@ -4,6 +4,9 @@ on: pull_request: types: - closed + branches-ignore: + - 'uni-pr' + - 'feature/unicorn-app' defaults: run: diff --git a/.github/workflows/pullrequest-lint.yml b/.github/workflows/pullrequest-lint.yml index f7b04171a1c8..41e94022f624 100644 --- a/.github/workflows/pullrequest-lint.yml +++ b/.github/workflows/pullrequest-lint.yml @@ -6,6 +6,9 @@ on: - reopened - edited - synchronize + branches-ignore: + - 'uni-pr' + - 'feature/unicorn-app' defaults: run: diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 71eace37971b..b0de5846c2a2 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -1,7 +1,10 @@ name: Monorepo pipeline - pull request on: - pull_request: {} + pull_request: + branches-ignore: + - 'uni-pr' + - 'feature/unicorn-app' workflow_dispatch: {} defaults: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 15225953329b..526291f1f2fa 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,6 +6,8 @@ on: - 'main' - 'release/**' - 'pre-release/**' + - '!uni-pr' + - '!feature/unicorn-app' paths-ignore: - '**/*.md' tags: @@ -13,6 +15,9 @@ on: workflow_dispatch: create: pull_request: + branches: + - '!uni-pr' + - '!feature/unicorn-app' types: - opened - synchronize diff --git a/.github/workflows/revert-pr.yaml b/.github/workflows/revert-pr.yaml index 492ca4300e92..5945712005b5 100644 --- a/.github/workflows/revert-pr.yaml +++ b/.github/workflows/revert-pr.yaml @@ -8,7 +8,11 @@ on: - completed branches: - main + - '!uni-pr' + - '!feature/unicorn-app' pull_request: + branches: + - '!uni-pr' types: - opened - synchronize diff --git a/.github/workflows/unicorns.yml b/.github/workflows/unicorns.yml new file mode 100644 index 000000000000..2bf69e51d595 --- /dev/null +++ b/.github/workflows/unicorns.yml @@ -0,0 +1,106 @@ +name: Unicorn CI/CD pipeline +on: + workflow_dispatch: + create: + pull_request: + types: + - opened + - synchronize + - labeled + - closed + pull_request_review: + types: [submitted] +concurrency: + # See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-a-fallback-value + group: push-unicorn${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +defaults: + run: + shell: bash +jobs: + printJob: + name: Print event + runs-on: ubuntu-latest + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: | + echo "$GITHUB_CONTEXT" + check-approved: + name: Is PR approved + runs-on: ubuntu-latest + outputs: + IS_APPROVED: ${{ steps.check-approved.outputs.result }} + steps: + - name: Check if PR is approved + id: check-approved + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + if (!context || !context.payload || !context.payload.pull_request) { + return false; + } + var reviews = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + if(!reviews || !reviews.data == null) { + return false; + } + var declined = reviews.data.filter(review => review.state != 'APPROVED').length > 0; + var pending = context.payload.pull_request.requested_teams.length > 0 + console.log(!(declined && pending) ? 'Pr is approved' : 'Pr is not approved'); + return !(declined && pending); + - name: output + run: echo "IS_APPROVED=${{ steps.check-approved.outputs.result }}" >> $GITHUB_OUTPUT + + check-unicorn: + name: Is this a unicorn PR + needs: check-approved + if: ${{ needs.check-approved.outputs.IS_APPROVED }}" + runs-on: ec2-runners + container: + image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest + timeout-minutes: 10 + steps: + - run: echo "selected runner = ${{ runner.name }}" + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Setup yarn + run: corepack enable + + - name: Get cache + id: get-cache + uses: ./.github/actions/get-cache + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + enable-cache: 'node_modules,generated-files' + + - name: Check unicorn affected + id: unicorn-affected + env: + BaseRef: ${{ github.base_ref }} #The target branch e.g. main (feature/unicorn-app) + HeadRef: ${{ github.head_ref }} #The branch being merged e.g. (unicorn-pipe-rel3) + run: | + echo "Comparing nx affected for $HeadRef using origin/$BaseRef as base branch" + echo IS_UNICORN=$(node scripts/ci/unicorn-utils.mjs "{\"baseBranch\": \"origin/$BaseRef\", \"mergeBranch\": \"$HeadRef\" }") >> "$GITHUB_OUTPUT" + - name: Results + run: | + echo "Unicorn = ${{ steps.unicorn-affected.outputs.IS_UNICORN }}" + + - name: Find Latest Release Branch + run: | + node scripts/ci/get-last-release.mjs $(git branch -r) + + - run: "echo 'latest release: ${{ steps.get_latest_release.outputs.data }}'" diff --git a/.gitignore b/.gitignore index 46c71ffd1796..a31e5dd70833 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ TODO .env .env.* .envrc.private +nx-cloud.env .nvmrc .node-version # IDE - VSCode diff --git a/apps/unicorn-app/.eslintrc.json b/apps/unicorn-app/.eslintrc.json new file mode 100644 index 000000000000..b0b7131a6c3a --- /dev/null +++ b/apps/unicorn-app/.eslintrc.json @@ -0,0 +1,55 @@ +{ + "extends": ["plugin:@nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "rules": { + "@nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allowCircularSelfDependency": true, + "allow": ["../../../infra/src/dsl"], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"] + } + ] + } + ], + "simple-import-sort/imports": [ + "warn", + { + "groups": [ + // React related packages come first, followed by all external imports. + ["^react", "^\\w", "^@(?!island).+"], + // Then island.is packages. + ["^(@island.is).*"], + // Then all other imports in this order: "../", "./", "./css" + [ + "^\\.\\.(?!/?$)", + "^\\.\\./?$", + "^\\./(?=.*/)(?!/?$)", + "^\\.(?!/?$)", + "^\\./?$", + "^.+\\.?(css)$" + ] + ] + } + ] + }, + "plugins": ["simple-import-sort"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/unicorn-app/codegen.yml b/apps/unicorn-app/codegen.yml new file mode 100644 index 000000000000..bcc1fe6a884d --- /dev/null +++ b/apps/unicorn-app/codegen.yml @@ -0,0 +1,24 @@ +schema: + - apps/api/src/api.graphql +documents: apps/unicorn-app/src/queries/*.{ts,tsx} +generates: + apps/web/graphql/schema.ts: + plugins: + - typescript + - typescript-operations + config: + exportFragmentSpreadSubTypes: true + scalars: + DateTime: Date + JSON: '{ [key: string]: any }' + namingConvention: + typeNames: change-case#pascalCase + apps/web/graphql/fragmentTypes.json: + plugins: + - fragment-matcher + config: + module: commonjs + apolloClientVersion: 3 +hooks: + afterAllFileWrite: + - prettier --write diff --git a/apps/unicorn-app/index.d.ts b/apps/unicorn-app/index.d.ts new file mode 100644 index 000000000000..a9ab53489c7f --- /dev/null +++ b/apps/unicorn-app/index.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +declare module '*.svg' { + const content: any + export const ReactComponent: any + export default content +} diff --git a/apps/unicorn-app/infra/infra.ts b/apps/unicorn-app/infra/infra.ts new file mode 100644 index 000000000000..47fdfe93c611 --- /dev/null +++ b/apps/unicorn-app/infra/infra.ts @@ -0,0 +1,34 @@ +import { service, ServiceBuilder } from '../../../infra/src/dsl/dsl' +const serviceName = 'unicorn-app' +export const serviceSetup = (): ServiceBuilder => + service(serviceName) + .image(serviceName) + .namespace(serviceName) + .serviceAccount(serviceName) + .ingress({ + primary: { + host: { + dev: serviceName, + staging: serviceName, + prod: serviceName, + }, + paths: ['/'], + }, + }) + .replicaCount({ + default: 1, + min: 1, + max: 10, + }) + .resources({ + limits: { + cpu: '200m', + memory: '256Mi', + }, + requests: { + cpu: '50m', + memory: '128Mi', + }, + }) + .liveness('/liveness') + .readiness('/readiness') diff --git a/apps/unicorn-app/jest.config.ts b/apps/unicorn-app/jest.config.ts new file mode 100644 index 000000000000..5705a6bac0c1 --- /dev/null +++ b/apps/unicorn-app/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'unicorn-app', + preset: '../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/apps/unicorn-app', +} diff --git a/apps/unicorn-app/next-env.d.ts b/apps/unicorn-app/next-env.d.ts new file mode 100644 index 000000000000..4f11a03dc6cc --- /dev/null +++ b/apps/unicorn-app/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/unicorn-app/next.config.js b/apps/unicorn-app/next.config.js new file mode 100644 index 000000000000..5dd157afdc66 --- /dev/null +++ b/apps/unicorn-app/next.config.js @@ -0,0 +1,22 @@ +//@ts-check + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { composePlugins, withNx } = require('@nx/next') + +/** + * @type {import('@nx/next/plugins/with-nx').WithNxOptions} + **/ +const nextConfig = { + nx: { + // Set this to true if you would like to use SVGR + // See: https://github.com/gregberge/svgr + svgr: false, + }, +} + +const plugins = [ + // Add more Next.js plugins to this list if needed. + withNx, +] + +module.exports = composePlugins(...plugins)(nextConfig) diff --git a/apps/unicorn-app/project.json b/apps/unicorn-app/project.json new file mode 100644 index 000000000000..c7b92fdd580a --- /dev/null +++ b/apps/unicorn-app/project.json @@ -0,0 +1,101 @@ +{ + "name": "unicorn-app", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/unicorn-app", + "projectType": "application", + "tags": ["scope:react-next"], + "targets": { + "build": { + "executor": "@nx/next:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/apps/unicorn-app" + }, + "configurations": { + "development": { + "outputPath": "apps/unicorn-app" + }, + "production": {} + }, + "dependsOn": ["build-custom-server"] + }, + "build-custom-server": { + "executor": "@nx/webpack:webpack", + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/apps/unicorn-app", + "main": "apps/unicorn-app/server.ts", + "tsConfig": "apps/unicorn-app/tsconfig.server.json", + "maxWorkers": 2, + "assets": [], + "compiler": "tsc", + "target": "node", + "webpackConfig": "apps/unicorn-app/webpack.config.js" + }, + "configurations": { + "development": {}, + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "serve": { + "executor": "@nx/next:server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "unicorn-app:build", + "dev": true, + "customServerTarget": "unicorn-app:serve-custom-server" + }, + "configurations": { + "development": { + "buildTarget": "unicorn-app:build:development", + "dev": true, + "customServerTarget": "unicorn-app:serve-custom-server:development" + }, + "production": { + "buildTarget": "unicorn-app:build:production", + "dev": false, + "customServerTarget": "unicorn-app:serve-custom-server:production" + } + } + }, + "serve-custom-server": { + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "options": { + "buildTarget": "unicorn-app:build-custom-server" + }, + "configurations": { + "development": { + "buildTarget": "unicorn-app:build-custom-server:development" + }, + "production": { + "buildTarget": "unicorn-app:build-custom-server:production" + } + } + }, + "export": { + "executor": "@nx/next:export", + "options": { + "buildTarget": "unicorn-app:build:production" + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/apps/unicorn-app"], + "options": { + "jestConfig": "apps/unicorn-app/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "docker-next": { + "executor": "Intentionally left blank, only so this target is valid when using `nx show projects --with-target docker-next`" + } + } +} diff --git a/apps/unicorn-app/public/.gitkeep b/apps/unicorn-app/public/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/apps/unicorn-app/public/favicon.ico b/apps/unicorn-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..317ebcb2336e0833a22dddf0ab287849f26fda57 GIT binary patch literal 15086 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 { + it('should render successfully', () => { + const { baseElement } = render() + expect(baseElement).toBeTruthy() + }) +}) diff --git a/apps/unicorn-app/src/app/api/hello/route.ts b/apps/unicorn-app/src/app/api/hello/route.ts new file mode 100644 index 000000000000..4a0d8ceed763 --- /dev/null +++ b/apps/unicorn-app/src/app/api/hello/route.ts @@ -0,0 +1,3 @@ +export async function GET(request: Request) { + return new Response('Hello, hello, from API!') +} diff --git a/apps/unicorn-app/src/app/global.css b/apps/unicorn-app/src/app/global.css new file mode 100644 index 000000000000..2a8880df280a --- /dev/null +++ b/apps/unicorn-app/src/app/global.css @@ -0,0 +1,409 @@ +html { + -webkit-text-size-adjust: 100%; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, + Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + line-height: 1.5; + tab-size: 4; + scroll-behavior: smooth; +} +body { + font-family: inherit; + line-height: inherit; + margin: 0; +} +h1, +h2, +p, +pre { + margin: 0; +} +*, +::before, +::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: currentColor; +} +h1, +h2 { + font-size: inherit; + font-weight: inherit; +} +a { + color: inherit; + text-decoration: inherit; +} +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + Liberation Mono, Courier New, monospace; +} +svg { + display: block; + vertical-align: middle; + shape-rendering: auto; + text-rendering: optimizeLegibility; +} +pre { + background-color: rgba(55, 65, 81, 1); + border-radius: 0.25rem; + color: rgba(229, 231, 235, 1); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + Liberation Mono, Courier New, monospace; + overflow: scroll; + padding: 0.5rem 0.75rem; +} + +.shadow { + box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} +.rounded { + border-radius: 1.5rem; +} +.wrapper { + width: 100%; +} +.container { + margin-left: auto; + margin-right: auto; + max-width: 768px; + padding-bottom: 3rem; + padding-left: 1rem; + padding-right: 1rem; + color: rgba(55, 65, 81, 1); + width: 100%; +} +#welcome { + margin-top: 2.5rem; +} +#welcome h1 { + font-size: 3rem; + font-weight: 500; + letter-spacing: -0.025em; + line-height: 1; +} +#welcome span { + display: block; + font-size: 3.875rem; + font-weight: 300; + line-height: 2.25rem; + margin-bottom: 0.5rem; +} +#hero { + align-items: center; + background-color: hsla(214, 62%, 21%, 1); + border: none; + box-sizing: border-box; + color: rgba(55, 65, 81, 1); + display: grid; + grid-template-columns: 1fr; + margin-top: 3.5rem; +} +#hero .text-container { + color: rgba(255, 255, 255, 1); + padding: 3rem 2rem; +} +#hero .text-container h2 { + font-size: 1.5rem; + line-height: 2rem; + position: relative; +} +#hero .text-container h2 svg { + color: hsla(162, 47%, 50%, 1); + height: 2rem; + left: -0.25rem; + position: absolute; + top: 0; + width: 2rem; +} +#hero .text-container h2 span { + margin-left: 2.5rem; +} +#hero .text-container a { + background-color: rgba(255, 255, 255, 1); + border-radius: 0.75rem; + color: rgba(55, 65, 81, 1); + display: inline-block; + margin-top: 1.5rem; + padding: 1rem 2rem; + text-decoration: inherit; +} +#hero .logo-container { + display: none; + justify-content: center; + padding-left: 2rem; + padding-right: 2rem; +} +#hero .logo-container svg { + color: rgba(255, 255, 255, 1); + width: 66.666667%; +} +#middle-content { + align-items: flex-start; + display: grid; + gap: 4rem; + grid-template-columns: 1fr; + margin-top: 3.5rem; +} +#learning-materials { + padding: 2.5rem 2rem; +} +#learning-materials h2 { + font-weight: 500; + font-size: 1.25rem; + letter-spacing: -0.025em; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; +} +.list-item-link { + align-items: center; + border-radius: 0.75rem; + display: flex; + margin-top: 1rem; + padding: 1rem; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + width: 100%; +} +.list-item-link svg:first-child { + margin-right: 1rem; + height: 1.5rem; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + width: 1.5rem; +} +.list-item-link > span { + flex-grow: 1; + font-weight: 400; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.list-item-link > span > span { + color: rgba(107, 114, 128, 1); + display: block; + flex-grow: 1; + font-size: 0.75rem; + font-weight: 300; + line-height: 1rem; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.list-item-link svg:last-child { + height: 1rem; + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + width: 1rem; +} +.list-item-link:hover { + color: rgba(255, 255, 255, 1); + background-color: hsla(162, 47%, 50%, 1); +} +.list-item-link:hover > span { +} +.list-item-link:hover > span > span { + color: rgba(243, 244, 246, 1); +} +.list-item-link:hover svg:last-child { + transform: translateX(0.25rem); +} +#other-links { +} +.button-pill { + padding: 1.5rem 2rem; + transition-duration: 300ms; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + align-items: center; + display: flex; +} +.button-pill svg { + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; + flex-shrink: 0; + width: 3rem; +} +.button-pill > span { + letter-spacing: -0.025em; + font-weight: 400; + font-size: 1.125rem; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; +} +.button-pill span span { + display: block; + font-size: 0.875rem; + font-weight: 300; + line-height: 1.25rem; +} +.button-pill:hover svg, +.button-pill:hover { + color: rgba(255, 255, 255, 1) !important; +} +#nx-console:hover { + background-color: rgba(0, 122, 204, 1); +} +#nx-console svg { + color: rgba(0, 122, 204, 1); +} +#nx-console-jetbrains { + margin-top: 2rem; +} +#nx-console-jetbrains:hover { + background-color: rgba(255, 49, 140, 1); +} +#nx-console-jetbrains svg { + color: rgba(255, 49, 140, 1); +} +#nx-repo:hover { + background-color: rgba(24, 23, 23, 1); +} +#nx-repo svg { + color: rgba(24, 23, 23, 1); +} +#nx-cloud { + margin-bottom: 2rem; + margin-top: 2rem; + padding: 2.5rem 2rem; +} +#nx-cloud > div { + align-items: center; + display: flex; +} +#nx-cloud > div svg { + border-radius: 0.375rem; + flex-shrink: 0; + width: 3rem; +} +#nx-cloud > div h2 { + font-size: 1.125rem; + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; +} +#nx-cloud > div h2 span { + display: block; + font-size: 0.875rem; + font-weight: 300; + line-height: 1.25rem; +} +#nx-cloud p { + font-size: 1rem; + line-height: 1.5rem; + margin-top: 1rem; +} +#nx-cloud pre { + margin-top: 1rem; +} +#nx-cloud a { + color: rgba(107, 114, 128, 1); + display: block; + font-size: 0.875rem; + line-height: 1.25rem; + margin-top: 1.5rem; + text-align: right; +} +#nx-cloud a:hover { + text-decoration: underline; +} +#commands { + padding: 2.5rem 2rem; + margin-top: 3.5rem; +} +#commands h2 { + font-size: 1.25rem; + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1.75rem; + padding-left: 1rem; + padding-right: 1rem; +} +#commands p { + font-size: 1rem; + font-weight: 300; + line-height: 1.5rem; + margin-top: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} +details { + align-items: center; + display: flex; + margin-top: 1rem; + padding-left: 1rem; + padding-right: 1rem; + width: 100%; +} +details pre > span { + color: rgba(181, 181, 181, 1); + display: block; +} +summary { + border-radius: 0.5rem; + display: flex; + font-weight: 400; + padding: 0.5rem; + cursor: pointer; + transition-property: background-color, border-color, color, fill, stroke, + opacity, box-shadow, transform, filter, backdrop-filter, + -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +summary:hover { + background-color: rgba(243, 244, 246, 1); +} +summary svg { + height: 1.5rem; + margin-right: 1rem; + width: 1.5rem; +} +#love { + color: rgba(107, 114, 128, 1); + font-size: 0.875rem; + line-height: 1.25rem; + margin-top: 3.5rem; + opacity: 0.6; + text-align: center; +} +#love svg { + color: rgba(252, 165, 165, 1); + width: 1.25rem; + height: 1.25rem; + display: inline; + margin-top: -0.25rem; +} +@media screen and (min-width: 768px) { + #hero { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + #hero .logo-container { + display: flex; + } + #middle-content { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} diff --git a/apps/unicorn-app/src/app/layout.tsx b/apps/unicorn-app/src/app/layout.tsx new file mode 100644 index 000000000000..43b373a18bf6 --- /dev/null +++ b/apps/unicorn-app/src/app/layout.tsx @@ -0,0 +1,18 @@ +import './global.css' + +export const metadata = { + title: 'Welcome to Unicorn app', + description: 'Generated by create-nx-workspace', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/apps/unicorn-app/src/app/page.module.css b/apps/unicorn-app/src/app/page.module.css new file mode 100644 index 000000000000..8a13e21cb311 --- /dev/null +++ b/apps/unicorn-app/src/app/page.module.css @@ -0,0 +1,2 @@ +.page { +} diff --git a/apps/unicorn-app/src/app/page.tsx b/apps/unicorn-app/src/app/page.tsx new file mode 100644 index 000000000000..5d9289cb0058 --- /dev/null +++ b/apps/unicorn-app/src/app/page.tsx @@ -0,0 +1,17 @@ +export default function Index() { + return ( +
+
+
+
+

+ + Welcome unicorn 🦄 + +

+
+
+
+
+ ) +} diff --git a/apps/unicorn-app/tsconfig.json b/apps/unicorn-app/tsconfig.json new file mode 100644 index 000000000000..37e77ed8c826 --- /dev/null +++ b/apps/unicorn-app/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "preserve", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "types": ["node", "jest"], + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "resolveJsonModule": true, + "isolatedModules": true, + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.js", + "**/*.jsx", + "next-env.d.ts", + ".next/types/**/*.ts" + ], + "exclude": ["node_modules", "jest.config.ts"] +} diff --git a/apps/unicorn-app/tsconfig.server.json b/apps/unicorn-app/tsconfig.server.json new file mode 100644 index 000000000000..8fafdb4be36d --- /dev/null +++ b/apps/unicorn-app/tsconfig.server.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "noEmit": false, + "incremental": true, + "tsBuildInfoFile": "../../tmp/buildcache/apps/unicorn-app/server", + "types": ["node"] + }, + "include": ["server.ts"] +} diff --git a/apps/unicorn-app/tsconfig.spec.json b/apps/unicorn-app/tsconfig.spec.json new file mode 100644 index 000000000000..214b2cc208c1 --- /dev/null +++ b/apps/unicorn-app/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "jsx": "react" + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/apps/unicorn-app/webpack.config.js b/apps/unicorn-app/webpack.config.js new file mode 100644 index 000000000000..95e68d080f5b --- /dev/null +++ b/apps/unicorn-app/webpack.config.js @@ -0,0 +1,8 @@ +const { composePlugins, withNx } = require('@nx/webpack') + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), (config) => { + // Note: This was added by an Nx migration. Webpack builds are required to have a corresponding Webpack config file. + // See: https://nx.dev/recipes/webpack/webpack-config-setup + return config +}) diff --git a/charts/islandis/values.dev.yaml b/charts/islandis/values.dev.yaml index e0f5e81d1937..29be8c5e25f0 100644 --- a/charts/islandis/values.dev.yaml +++ b/charts/islandis/values.dev.yaml @@ -1810,6 +1810,7 @@ namespaces: - 'services-sessions' - 'contentful-apps' - 'services-university-gateway' + - 'unicorn-app' - 'portals-my-pages' portals-admin: enabled: true @@ -3154,6 +3155,69 @@ skilavottord-ws: securityContext: allowPrivilegeEscalation: false privileged: false +unicorn-app: + enabled: true + env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: [] + grantNamespacesEnabled: false + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 1 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.dev01.devland.is' + paths: + - '/' + namespace: 'unicorn-app' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 1 + max: 10 + min: 1 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' + secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/unicorn-app' + create: true + name: 'unicorn-app' user-notification: args: - '--no-experimental-fetch' diff --git a/charts/islandis/values.prod.yaml b/charts/islandis/values.prod.yaml index d08668a62457..ac8712e81ede 100644 --- a/charts/islandis/values.prod.yaml +++ b/charts/islandis/values.prod.yaml @@ -1674,6 +1674,7 @@ namespaces: - 'services-university-gateway' - 'contentful-apps' - 'contentful-entry-tagger' + - 'unicorn-app' - 'portals-my-pages' portals-admin: enabled: true @@ -3033,6 +3034,69 @@ skilavottord-ws: securityContext: allowPrivilegeEscalation: false privileged: false +unicorn-app: + enabled: true + env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' + grantNamespaces: [] + grantNamespacesEnabled: false + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 1 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.island.is' + paths: + - '/' + namespace: 'unicorn-app' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 1 + max: 10 + min: 1 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' + secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/unicorn-app' + create: true + name: 'unicorn-app' user-notification: args: - '--no-experimental-fetch' diff --git a/charts/islandis/values.staging.yaml b/charts/islandis/values.staging.yaml index d1c2a12649f8..2a2928d07132 100644 --- a/charts/islandis/values.staging.yaml +++ b/charts/islandis/values.staging.yaml @@ -1548,6 +1548,7 @@ namespaces: - 'license-api' - 'services-sessions' - 'services-university-gateway' + - 'unicorn-app' - 'portals-my-pages' portals-admin: enabled: true @@ -2892,6 +2893,69 @@ skilavottord-ws: securityContext: allowPrivilegeEscalation: false privileged: false +unicorn-app: + enabled: true + env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' + grantNamespaces: [] + grantNamespacesEnabled: false + healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 + hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 3 + min: 1 + image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' + ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.staging01.devland.is' + paths: + - '/' + namespace: 'unicorn-app' + podDisruptionBudget: + maxUnavailable: 1 + podSecurityContext: + fsGroup: 65534 + pvcs: [] + replicaCount: + default: 1 + max: 3 + min: 1 + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' + secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' + securityContext: + allowPrivilegeEscalation: false + privileged: false + serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/unicorn-app' + create: true + name: 'unicorn-app' user-notification: args: - '--no-experimental-fetch' diff --git a/charts/services/unicorn-app/values.dev.yaml b/charts/services/unicorn-app/values.dev.yaml new file mode 100644 index 000000000000..345b91dd5ddb --- /dev/null +++ b/charts/services/unicorn-app/values.dev.yaml @@ -0,0 +1,81 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'dev' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'unicorn-app' +enabled: true +env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' +grantNamespaces: [] +grantNamespacesEnabled: false +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 1 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.dev01.devland.is' + paths: + - '/' +namespace: 'unicorn-app' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 1 + max: 10 + min: 1 +resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' +secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::013313053092:role/unicorn-app' + create: true + name: 'unicorn-app' diff --git a/charts/services/unicorn-app/values.prod.yaml b/charts/services/unicorn-app/values.prod.yaml new file mode 100644 index 000000000000..f9782daf09fc --- /dev/null +++ b/charts/services/unicorn-app/values.prod.yaml @@ -0,0 +1,81 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'prod' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'unicorn-app' +enabled: true +env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: 'driving-license-use-v1-endpoint-for-v2-comms' +grantNamespaces: [] +grantNamespacesEnabled: false +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 10 + min: 1 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.island.is' + paths: + - '/' +namespace: 'unicorn-app' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 1 + max: 10 + min: 1 +resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' +secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::251502586493:role/unicorn-app' + create: true + name: 'unicorn-app' diff --git a/charts/services/unicorn-app/values.staging.yaml b/charts/services/unicorn-app/values.staging.yaml new file mode 100644 index 000000000000..50960c303bef --- /dev/null +++ b/charts/services/unicorn-app/values.staging.yaml @@ -0,0 +1,81 @@ +##################################################################### +# +# Do not edit this file manually, it is automatically generated. +# Run "yarn charts" instead. +# +##################################################################### + +global: + env: + AUDIT_GROUP_NAME: '/island-is/audit-log' + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' + PORT: '3333' + name: 'staging' + initContainer: + env: + AWS_REGION: 'eu-west-1' + NPM_CONFIG_UPDATE_NOTIFIER: 'false' +name: 'unicorn-app' +enabled: true +env: + LOG_LEVEL: 'info' + NODE_OPTIONS: '--max-old-space-size=230 -r dd-trace/init' + SERVERSIDE_FEATURES_ON: '' +grantNamespaces: [] +grantNamespacesEnabled: false +healthCheck: + liveness: + initialDelaySeconds: 3 + path: '/liveness' + timeoutSeconds: 3 + readiness: + initialDelaySeconds: 3 + path: '/readiness' + timeoutSeconds: 3 +hpa: + scaling: + metric: + cpuAverageUtilization: 90 + nginxRequestsIrate: 5 + replicas: + max: 3 + min: 1 +image: + repository: '821090935708.dkr.ecr.eu-west-1.amazonaws.com/unicorn-app' +ingress: + primary-alb: + annotations: + kubernetes.io/ingress.class: 'nginx-external-alb' + nginx.ingress.kubernetes.io/service-upstream: 'true' + hosts: + - host: 'unicorn-app.staging01.devland.is' + paths: + - '/' +namespace: 'unicorn-app' +podDisruptionBudget: + maxUnavailable: 1 +podSecurityContext: + fsGroup: 65534 +pvcs: [] +replicaCount: + default: 1 + max: 3 + min: 1 +resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '50m' + memory: '128Mi' +secrets: + CONFIGCAT_SDK_KEY: '/k8s/configcat/CONFIGCAT_SDK_KEY' +securityContext: + allowPrivilegeEscalation: false + privileged: false +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: 'arn:aws:iam::261174024191:role/unicorn-app' + create: true + name: 'unicorn-app' diff --git a/infra/src/uber-charts/islandis.ts b/infra/src/uber-charts/islandis.ts index 569b921bc2bf..db2d1ecc6940 100644 --- a/infra/src/uber-charts/islandis.ts +++ b/infra/src/uber-charts/islandis.ts @@ -62,6 +62,7 @@ import { } from '../../../apps/services/sessions/infra/sessions' import { serviceSetup as authAdminApiSetup } from '../../../apps/services/auth/admin-api/infra/auth-admin-api' +import { serviceSetup as unicornAppSetup } from '../../../apps/unicorn-app/infra/infra' import { EnvironmentServices } from '.././dsl/types/charts' import { ServiceBuilder } from '../dsl/dsl' @@ -146,6 +147,8 @@ const userNotificationWorkerService = userNotificationWorkerSetup({ const userNotificationCleanupWorkerService = userNotificationCleanUpWorkerSetup() +const unicornApp = unicornAppSetup() + const githubActionsCache = githubActionsCacheSetup() const externalContractsTests = externalContractsTestsSetup() @@ -186,6 +189,7 @@ export const Services: EnvironmentServices = { contentfulApps, contentfulEntryTagger, bffAdminPortalService, + unicornApp, bffServicePortalService, ], staging: [ @@ -221,6 +225,7 @@ export const Services: EnvironmentServices = { universityGatewayService, universityGatewayWorker, bffAdminPortalService, + unicornApp, bffServicePortalService, ], dev: [ @@ -260,6 +265,7 @@ export const Services: EnvironmentServices = { universityGatewayService, universityGatewayWorker, bffAdminPortalService, + unicornApp, bffServicePortalService, ], } diff --git a/scripts/ci/create-release.mjs b/scripts/ci/create-release.mjs new file mode 100644 index 000000000000..85d818b14889 --- /dev/null +++ b/scripts/ci/create-release.mjs @@ -0,0 +1,35 @@ +import { Octokit } from '@octokit/rest' + +const { GITHUB_TOKEN, GITHUB_REPOSITORY, GITHUB_REF } = process.env +const octokit = new Octokit({ auth: GITHUB_TOKEN }) +const [owner, repo] = GITHUB_REPOSITORY?.split('/') || [] + +const arg = process.argv.slice(2) + +const { data: pullRequest } = await octokit.rest.pulls.get({ + owner: owner, + repo: repo, + pull_number: arg[0], +}) + +const SHA = pullRequest.head.sha +// This is a temporary commit that is created behind the scenes for +// the test merge that validated no conflicts exist with the base branch. +// It is not committed to the repository. +// After the PR is merged, this value instead represents the SHA of the merge commit +octokit.rest.repos + .createRelease({ + owner: owner, + repo: repo, + target_commitish: SHA, + tag_name: 'SomeTag', + name: 'Test 123', + generate_release_notes: true, + }) + .then(({ data }) => { + console.log(data) + }) + .catch((error) => { + console.log(error) + process.exit(1) + }) diff --git a/scripts/ci/get-last-release.mjs b/scripts/ci/get-last-release.mjs new file mode 100644 index 000000000000..03feaf63f4d0 --- /dev/null +++ b/scripts/ci/get-last-release.mjs @@ -0,0 +1,17 @@ +const releases = process.argv + .slice(2) + .map((release) => release.replace('origin/release/', '')) + .filter((release) => /^\d+\.\d+\.\d+$/.test(release)) + .sort((a, b) => { + const [aMajor, aMinor, aPatch] = a.split('.').map(Number) + const [bMajor, bMinor, bPatch] = b.split('.').map(Number) + + if (aMajor !== bMajor) { + return bMajor - aMajor + } else if (aMinor !== bMinor) { + return bMinor - aMinor + } else { + return bPatch - aPatch + } + }) +console.log(releases[0]) diff --git a/scripts/ci/unicorn-utils.mjs b/scripts/ci/unicorn-utils.mjs new file mode 100644 index 000000000000..22078efe215d --- /dev/null +++ b/scripts/ci/unicorn-utils.mjs @@ -0,0 +1,17 @@ +import { execSync } from 'child_process' +import { workspaceRoot } from '@nx/devkit' + +const unicornApps = ['unicorn-app'] + +const arg = JSON.parse(process.argv.slice(2)) +try { + const affected = JSON.parse( + execSync( + `cd ${workspaceRoot} && yarn nx show projects --affected --base ${arg.baseBranch} --json | jq -r`, + ).toString(), + ) + console.log(affected.some((item) => unicornApps.includes(item))) +} catch (e) { + console.error(e.message) + process.exit(1) +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 7be0f1c28845..32541263a4c8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1126,6 +1126,7 @@ "@island.is/testing/e2e": ["libs/testing/e2e/src/index.ts"], "@island.is/testing/fixtures": ["libs/testing/fixtures/src/index.ts"], "@island.is/testing/nest": ["libs/testing/nest/src/index.ts"], + "@island.is/unicorn-app/*": ["apps/unicorn-app/*"], "@island.is/university-gateway": ["libs/university-gateway/src/index.ts"], "@island.is/user-monitoring": ["libs/user-monitoring/src/index.ts"], "@island.is/web/*": ["apps/web/*"],