├── docs ├── assets │ └── .gitkeep ├── .ruby-version ├── .gitignore ├── favicon.ico ├── nx-oc │ └── nx-oc.md ├── nx-release │ └── nx-release.md ├── index.md ├── nx-adsp │ └── nx-adsp.md ├── 404.html ├── Gemfile └── _config.yml ├── .eslintignore ├── packages ├── nx-adsp │ ├── src │ │ ├── index.ts │ │ ├── generators │ │ │ ├── angular-app │ │ │ │ ├── files │ │ │ │ │ ├── src │ │ │ │ │ │ ├── app │ │ │ │ │ │ │ ├── protected │ │ │ │ │ │ │ │ ├── protected.component.css__tmpl__ │ │ │ │ │ │ │ │ ├── protected.component.html__tmpl__ │ │ │ │ │ │ │ │ ├── protected.component.ts__tmpl__ │ │ │ │ │ │ │ │ └── protected.component.spec.ts__tmpl__ │ │ │ │ │ │ │ ├── logout │ │ │ │ │ │ │ │ ├── logout.component.html__tmpl__ │ │ │ │ │ │ │ │ └── logout.component.ts__tmpl__ │ │ │ │ │ │ │ ├── home │ │ │ │ │ │ │ │ ├── home.component.html__tmpl__ │ │ │ │ │ │ │ │ └── home.component.ts__tmpl__ │ │ │ │ │ │ │ ├── auth-callback │ │ │ │ │ │ │ │ ├── auth-callback.component.css__tmpl__ │ │ │ │ │ │ │ │ ├── auth-callback.component.html__tmpl__ │ │ │ │ │ │ │ │ ├── auth-callback.component.spec.ts__tmpl__ │ │ │ │ │ │ │ │ └── auth-callback.component.ts__tmpl__ │ │ │ │ │ │ │ ├── services │ │ │ │ │ │ │ │ ├── auth-guard.service.ts__tmpl__ │ │ │ │ │ │ │ │ └── auth.service.ts__tmpl__ │ │ │ │ │ │ │ ├── tenant.service.ts__tmpl__ │ │ │ │ │ │ │ ├── app.component.ts__tmpl__ │ │ │ │ │ │ │ ├── auth.interceptor.ts__tmpl__ │ │ │ │ │ │ │ ├── app.routes.ts__tmpl__ │ │ │ │ │ │ │ ├── app.component.css__tmpl__ │ │ │ │ │ │ │ ├── app.module.ts__tmpl__ │ │ │ │ │ │ │ ├── app.component.spec.ts__tmpl__ │ │ │ │ │ │ │ └── app.component.html__tmpl__ │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ ├── assets │ │ │ │ │ │ │ ├── banner.jpg │ │ │ │ │ │ │ └── github-1.svg │ │ │ │ │ │ ├── styles.css__tmpl__ │ │ │ │ │ │ ├── test-setup.ts__tmpl__ │ │ │ │ │ │ ├── main.ts__tmpl__ │ │ │ │ │ │ ├── environments │ │ │ │ │ │ │ ├── environment.ts__tmpl__ │ │ │ │ │ │ │ └── config.ts__tmpl__ │ │ │ │ │ │ └── index.html__tmpl__ │ │ │ │ │ └── nginx.conf__tmpl__ │ │ │ │ ├── schema.d.ts │ │ │ │ └── schema.json │ │ │ ├── react-app │ │ │ │ ├── files │ │ │ │ │ ├── src │ │ │ │ │ │ ├── renew.html__tmpl__ │ │ │ │ │ │ ├── renew.ts__tmpl__ │ │ │ │ │ │ ├── favicon.ico │ │ │ │ │ │ ├── assets │ │ │ │ │ │ │ └── banner.jpg │ │ │ │ │ │ ├── react-oidc.d.ts__tmpl__ │ │ │ │ │ │ ├── environments │ │ │ │ │ │ │ └── environment.ts__tmpl__ │ │ │ │ │ │ ├── index.html__tmpl__ │ │ │ │ │ │ ├── access.ts__tmpl__ │ │ │ │ │ │ ├── store.ts__tmpl__ │ │ │ │ │ │ ├── app │ │ │ │ │ │ │ ├── app.spec.tsx__tmpl__ │ │ │ │ │ │ │ ├── app.module.css__tmpl__ │ │ │ │ │ │ │ └── config.slice.ts__tmpl__ │ │ │ │ │ │ └── main.tsx__tmpl__ │ │ │ │ │ ├── webpack.config.js__tmpl__ │ │ │ │ │ └── nginx.conf__tmpl__ │ │ │ │ ├── schema.d.ts │ │ │ │ └── schema.json │ │ │ ├── dotnet-service │ │ │ │ ├── files │ │ │ │ │ ├── Examples │ │ │ │ │ │ ├── ServiceRoles.cs__tmpl__ │ │ │ │ │ │ ├── HelloWorldConfiguration.cs__tmpl__ │ │ │ │ │ │ └── HelloWorldEvent.cs__tmpl__ │ │ │ │ │ ├── appsettings.Development.json__tmpl__ │ │ │ │ │ ├── appsettings.json__tmpl__ │ │ │ │ │ └── Program.cs__tmpl__ │ │ │ │ ├── schema.d.ts │ │ │ │ ├── schema.json │ │ │ │ ├── dotnet-service.spec.ts │ │ │ │ └── dotnet-service.ts │ │ │ ├── express-service │ │ │ │ ├── schema.d.ts │ │ │ │ ├── files │ │ │ │ │ └── src │ │ │ │ │ │ ├── environments │ │ │ │ │ │ ├── environment.prod.ts__tmpl__ │ │ │ │ │ │ └── environment.ts__tmpl__ │ │ │ │ │ │ └── main.ts__tmpl__ │ │ │ │ ├── schema.json │ │ │ │ ├── express-service.spec.ts │ │ │ │ └── express-service.ts │ │ │ ├── react-form │ │ │ │ ├── schema.d.ts │ │ │ │ ├── files │ │ │ │ │ └── __fileName__ │ │ │ │ │ │ ├── __fileName__.slice.spec.ts__tmpl__ │ │ │ │ │ │ └── __fileName__.module.css__tmpl__ │ │ │ │ └── schema.json │ │ │ ├── react-task-list │ │ │ │ ├── schema.d.ts │ │ │ │ ├── files │ │ │ │ │ └── __fileName__ │ │ │ │ │ │ ├── task.d.ts__tmpl__ │ │ │ │ │ │ ├── __fileName__.slice.spec.ts__tmpl__ │ │ │ │ │ │ └── __fileName__.module.css__tmpl__ │ │ │ │ ├── schema.json │ │ │ │ └── react-task-list.spec.ts │ │ │ ├── mern │ │ │ │ ├── schema.d.ts │ │ │ │ ├── schema.json │ │ │ │ ├── mern.spec.ts │ │ │ │ └── mern.ts │ │ │ └── react-dotnet │ │ │ │ ├── schema.d.ts │ │ │ │ ├── schema.json │ │ │ │ ├── react-dotnet.spec.ts │ │ │ │ └── react-dotnet.ts │ │ └── utils │ │ │ ├── nginx.d.ts │ │ │ ├── task.d.ts │ │ │ ├── form.ts │ │ │ └── form.spec.ts │ ├── .babelrc │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── .releaserc.json │ ├── jest.config.ts │ ├── .eslintrc.json │ ├── package.json │ ├── README.md │ ├── project.json │ └── generators.json ├── nx-oc │ ├── .babelrc │ ├── src │ │ ├── pipeline-envs.ts │ │ ├── adsp │ │ │ ├── index.ts │ │ │ ├── adsp.d.ts │ │ │ └── environments.ts │ │ ├── index.ts │ │ ├── generators │ │ │ ├── apply-infra │ │ │ │ ├── schema.json │ │ │ │ ├── apply-infra.ts │ │ │ │ └── apply-infra.spec.ts │ │ │ ├── index.ts │ │ │ ├── deployment │ │ │ │ ├── node-files │ │ │ │ │ └── Dockerfile.template │ │ │ │ ├── frontend-files │ │ │ │ │ └── Dockerfile.template │ │ │ │ ├── dotnet-files │ │ │ │ │ └── Dockerfile.template │ │ │ │ ├── schema.d.ts │ │ │ │ └── schema.json │ │ │ └── pipeline │ │ │ │ ├── actions │ │ │ │ └── openshift │ │ │ │ │ ├── environment.infra.yml__tmpl__ │ │ │ │ │ └── environments.yml__tmpl__ │ │ │ │ ├── schema.d.ts │ │ │ │ ├── jenkins │ │ │ │ ├── environments.yml__tmpl__ │ │ │ │ └── environment.infra.yml__tmpl__ │ │ │ │ ├── schema.json │ │ │ │ ├── pipeline.ts │ │ │ │ └── pipeline.spec.ts │ │ ├── executors │ │ │ └── apply │ │ │ │ ├── schema.d.ts │ │ │ │ ├── schema.json │ │ │ │ ├── apply.spec.ts │ │ │ │ └── apply.ts │ │ └── utils │ │ │ ├── git-utils.ts │ │ │ └── oc-utils.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── executors.json │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── .releaserc.json │ ├── README.md │ ├── package.json │ ├── generators.json │ └── project.json ├── nx-release │ ├── .babelrc │ ├── src │ │ ├── index.ts │ │ ├── generators │ │ │ └── lib │ │ │ │ ├── schema.d.ts │ │ │ │ ├── root-files │ │ │ │ └── .releaserc.json__tmpl__ │ │ │ │ ├── files │ │ │ │ └── .releaserc.json__tmpl__ │ │ │ │ ├── schema.json │ │ │ │ ├── lib.spec.ts │ │ │ │ └── lib.ts │ │ └── release-plugin │ │ │ ├── index.ts │ │ │ ├── git-utils.ts │ │ │ ├── git-utils.spec.ts │ │ │ ├── types.d.ts │ │ │ ├── nx-utils.spec.ts │ │ │ ├── nx-util.ts │ │ │ ├── wrap-plugin.ts │ │ │ └── wrap-plugin.spec.ts │ ├── tsconfig.json │ ├── generators.json │ ├── tsconfig.lib.json │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── .releaserc.json │ ├── package.json │ ├── README.md │ └── project.json └── semantic-release-nuget │ ├── .babelrc │ ├── src │ ├── index.ts │ ├── types.d.ts │ └── plugin │ │ ├── config.ts │ │ ├── prepare.ts │ │ └── publish.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── .eslintrc.json │ ├── package.json │ ├── tsconfig.spec.json │ ├── jest.config.ts │ ├── .releaserc.json │ ├── README.md │ └── project.json ├── .prettierrc ├── .prettierignore ├── .vscode ├── extensions.json └── settings.json ├── e2e ├── nx-adsp-e2e │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── jest.config.js │ ├── project.json │ └── tests │ │ └── nx-adsp.spec.ts ├── nx-oc-e2e │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── jest.config.js │ ├── project.json │ └── tests │ │ └── nx-oc.spec.ts └── nx-release-e2e │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── jest.config.js │ ├── project.json │ └── tests │ └── nx-release.spec.ts ├── .env ├── .releaserc.json ├── jest.config.ts ├── .editorconfig ├── tools └── tsconfig.tools.json ├── .gitignore ├── jest.preset.js ├── tsconfig.base.json ├── README.md ├── .eslintrc.json ├── nx.json ├── .github └── workflows │ ├── pull-request.yml │ ├── release-ci.yml │ └── codeql-analysis.yml └── package.json /docs/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.6 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /packages/nx-adsp/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@nx/web/babel"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/nx-oc/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@nx/web/babel"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/nx-release/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@nx/web/babel"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/nx-release/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './release-plugin/index.js'; 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GovAlta/nx-tools/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /packages/semantic-release-nuget/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@nx/web/babel"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/nx-oc/src/pipeline-envs.ts: -------------------------------------------------------------------------------- 1 | export const pipelineEnvs = ['Dev', 'Test', 'Prod']; 2 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/protected/protected.component.css__tmpl__: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/nx-oc/nx-oc.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: NX OpenShift plugin 4 | nav_order: 3 5 | has_children: true 6 | --- 7 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/logout/logout.component.html__tmpl__: -------------------------------------------------------------------------------- 1 |
Logged Out !!
2 | -------------------------------------------------------------------------------- /docs/nx-release/nx-release.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: NX Release plugin 4 | nav_order: 4 5 | has_children: true 6 | --- 7 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/utils/nginx.d.ts: -------------------------------------------------------------------------------- 1 | export interface NginxProxyConfiguration { 2 | location: string; 3 | proxyPass: string; 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | 6 | /.nx/cache 7 | /.nx/workspace-data -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/renew.html__tmpl__: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/renew.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { processSilentRenew } from 'redux-oidc'; 2 | 3 | processSilentRenew(); 4 | -------------------------------------------------------------------------------- /packages/nx-oc/src/adsp/index.ts: -------------------------------------------------------------------------------- 1 | export type { AdspConfiguration, AdspOptions } from './adsp'; 2 | export * from './adsp-utils'; 3 | export * from './environments'; 4 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GovAlta/nx-tools/HEAD/packages/nx-adsp/src/generators/react-app/files/src/favicon.ico -------------------------------------------------------------------------------- /packages/nx-oc/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as deploymentGenerator } from './generators/deployment/deployment'; 2 | export * from './adsp'; 3 | export * from './generators'; 4 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/src/index.ts: -------------------------------------------------------------------------------- 1 | import { prepare } from './plugin/prepare'; 2 | import { publish } from './plugin/publish'; 3 | 4 | export { prepare, publish }; 5 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GovAlta/nx-tools/HEAD/packages/nx-adsp/src/generators/angular-app/files/src/favicon.ico -------------------------------------------------------------------------------- /packages/nx-adsp/src/utils/task.d.ts: -------------------------------------------------------------------------------- 1 | export interface QueueDefinition { 2 | namespace: string; 3 | name: string; 4 | assignerRoles: string[]; 5 | workerRoles: string[]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/assets/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GovAlta/nx-tools/HEAD/packages/nx-adsp/src/generators/react-app/files/src/assets/banner.jpg -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.vscode-typescript-tslint-plugin", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/assets/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GovAlta/nx-tools/HEAD/packages/nx-adsp/src/generators/angular-app/files/src/assets/banner.jpg -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # Feel free to add content and custom Front Matter to this file. 3 | # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults 4 | 5 | layout: home 6 | --- 7 | 8 | -------------------------------------------------------------------------------- /e2e/nx-adsp-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.spec.json" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Nx 18 enables using plugins to infer targets by default 2 | # This is disabled for existing workspaces to maintain compatibility 3 | # For more info, see: https://nx.dev/concepts/inferred-tasks 4 | NX_ADD_PLUGINS=false 5 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/Examples/ServiceRoles.cs__tmpl__: -------------------------------------------------------------------------------- 1 | namespace Adsp.Sdk.Examples; 2 | 3 | public static class ServiceRoles 4 | { 5 | public const string HelloWorlder = "hello-worlder"; 6 | } 7 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/apply-infra/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxOcApplyInfra", 4 | "title": "", 5 | "type": "object", 6 | "properties": {}, 7 | "required": [] 8 | } 9 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | project: string; 3 | } 4 | 5 | export interface NormalizedSchema extends Schema { 6 | projectRoot: string; 7 | projectDist: string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/home/home.component.html__tmpl__: -------------------------------------------------------------------------------- 1 |
2 |

Public Area

3 |
Making a Public API call to uptime endpoint
4 |
Uptime of API in seconds: {{uptime}}
5 | 6 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/styles.css__tmpl__: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "@abgov/web-components/index.css"; 3 | 4 | body { 5 | margin: 0px; 6 | } 7 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/protected/protected.component.html__tmpl__: -------------------------------------------------------------------------------- 1 |
2 |

Admin Area

3 |
Making a Protected API call to tenant endpoint using access token
4 |
Tenant Name: {{tenant.name}}
5 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/root-files/.releaserc.json__tmpl__: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "+([0-9])?(.{+([0-9]),x}).x", 4 | "main", 5 | {"name": "beta", "prerelease": true}, 6 | {"name": "alpha", "prerelease": true} 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/index.ts: -------------------------------------------------------------------------------- 1 | export { default as applyInfraGenerator } from './apply-infra/apply-infra'; 2 | export { default as deploymentGenerator } from './deployment/deployment'; 3 | export { default as pipelineGenerator } from './pipeline/pipeline'; 4 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/test-setup.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import 'zone.js'; 2 | import 'zone.js/dist/zone-testing'; 3 | import 'jest-preset-angular'; 4 | //https://github.com/thymikee/jest-preset-angular/issues/347 5 | import '@angular/localize/init'; 6 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "repositoryUrl": "git@github.com:GovAlta/nx-tools.git", 3 | "branches": [ 4 | "+([0-9])?(.{+([0-9]),x}).x", 5 | "main", 6 | {"name": "beta", "prerelease": true}, 7 | {"name": "alpha", "prerelease": true} 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /e2e/nx-oc-e2e/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /e2e/nx-adsp-e2e/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /e2e/nx-release-e2e/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | const { getJestProjects } = require('@nx/jest'); 2 | 3 | export default { 4 | projects: [ 5 | ...getJestProjects(), 6 | '/e2e/nx-adsp-e2e', 7 | '/e2e/nx-oc-e2e', 8 | '/e2e/nx-release-e2e', 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /packages/nx-oc/src/adsp/adsp.d.ts: -------------------------------------------------------------------------------- 1 | export interface AdspConfiguration { 2 | tenant: string; 3 | tenantRealm: string; 4 | accessServiceUrl: string; 5 | directoryServiceUrl: string; 6 | } 7 | 8 | export type AdspOptions = { 9 | adsp: AdspConfiguration; 10 | }; 11 | -------------------------------------------------------------------------------- /e2e/nx-oc-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.e2e.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-adsp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-oc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /docs/nx-adsp/nx-adsp.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: NX ADSP plugin 4 | nav_order: 2 5 | has_children: true 6 | --- 7 | 8 | # NX ADSP plugin 9 | 10 | ## Overview 11 | 12 | [Alberta Digital Service Platform (ADSP)](https://adsp.alberta.ca) 13 | 14 | `npm i -D @abgov/nx-adsp` 15 | -------------------------------------------------------------------------------- /e2e/nx-release-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.e2e.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/deployment/node-files/Dockerfile.template: -------------------------------------------------------------------------------- 1 | # This is a minimal Dockerfile for app deployment. 2 | FROM registry.access.redhat.com/ubi8/nodejs-18 3 | 4 | ARG PROJECT 5 | COPY ./dist/apps/${PROJECT} . 6 | COPY ./node_modules ./node_modules 7 | 8 | CMD node main.js 9 | -------------------------------------------------------------------------------- /packages/nx-release/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://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 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/actions/openshift/environment.infra.yml__tmpl__: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: ServiceAccount 6 | metadata: 7 | labels: 8 | app: pipeline 9 | name: github-actions 10 | namespace: <%= ocInfraProject %> 11 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/deployment/frontend-files/Dockerfile.template: -------------------------------------------------------------------------------- 1 | # This is a minimal Dockerfile for app deployment. 2 | FROM registry.access.redhat.com/ubi8/nginx-118 3 | 4 | ARG PROJECT 5 | COPY ./dist/apps/${PROJECT}/nginx.conf "${NGINX_CONF_PATH}" 6 | COPY ./dist/apps/${PROJECT} . 7 | 8 | CMD nginx -g "daemon off;" 9 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/logout/logout.component.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: '<%= projectName %>-app-logout', 5 | templateUrl: 'logout.component.html', 6 | }) 7 | export class LogoutComponent { 8 | isAuthenticated = false; 9 | } 10 | -------------------------------------------------------------------------------- /packages/nx-oc/src/executors/apply/schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface PipelineEnvironment { 2 | project: string; 3 | tag: string; 4 | } 5 | 6 | export interface Schema { 7 | ocProject: string | string[] | PipelineEnvironment[]; 8 | } 9 | 10 | export interface NormalizedSchema { 11 | ocProjects: PipelineEnvironment[]; 12 | } 13 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/deployment/dotnet-files/Dockerfile.template: -------------------------------------------------------------------------------- 1 | # This is a minimal Dockerfile for app deployment. 2 | FROM registry.access.redhat.com/ubi8/dotnet-60-runtime 3 | 4 | ARG PROJECT 5 | ARG ASSEMBLY 6 | 7 | COPY ./dist/apps/${PROJECT}/net6.0 . 8 | 9 | ENV ASSEMBLY ${ASSEMBLY} 10 | CMD dotnet ${ASSEMBLY} 11 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/Examples/HelloWorldConfiguration.cs__tmpl__: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Adsp.Sdk.Examples; 4 | 5 | public class HelloWorldConfiguration 6 | { 7 | [JsonPropertyName("responses")] 8 | public Dictionary? Responses { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /packages/nx-adsp/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], 10 | "include": ["**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/nx-oc/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], 10 | "include": ["**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/nx-oc/executors.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "executors": { 4 | "apply": { 5 | "implementation": "./src/executors/apply/apply", 6 | "schema": "./src/executors/apply/schema.json", 7 | "description": "Executor that applies deployment template to OpenShift." 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], 10 | "include": ["**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.css__tmpl__: -------------------------------------------------------------------------------- 1 | .info { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | } 6 | 7 | .token { 8 | font-family: monospace; 9 | background-color: beige; 10 | width: 600px; 11 | text-align: left; 12 | word-break: break-all; 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-oc/src/utils/git-utils.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process' 2 | 3 | export function getGitRemoteUrl(): string { 4 | try { 5 | const stdout = execSync( 6 | "git config --get remote.origin.url", 7 | { stdio: "pipe" } 8 | ).toString() 9 | 10 | return stdout 11 | } catch (e) { 12 | console.log(`Failed to execute git`, e) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/schema.d.ts: -------------------------------------------------------------------------------- 1 | import type { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | 3 | export interface Schema { 4 | name: string; 5 | env: EnvironmentName; 6 | accessToken?: string; 7 | } 8 | 9 | export interface NormalizedSchema extends Schema { 10 | projectName: string; 11 | projectRoot: string; 12 | adsp: AdspConfiguration; 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/schema.d.ts: -------------------------------------------------------------------------------- 1 | import type { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | 3 | export interface Schema { 4 | name: string; 5 | env: EnvironmentName; 6 | accessToken?: string; 7 | } 8 | 9 | export interface NormalizedSchema extends Schema { 10 | projectName: string; 11 | projectRoot: string; 12 | adsp: AdspConfiguration; 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-release/generators.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "name": "nx-release", 4 | "version": "0.0.1", 5 | "generators": { 6 | "lib": { 7 | "factory": "./src/generators/lib/lib", 8 | "schema": "./src/generators/lib/schema.json", 9 | "description": "Generator that adds release target to a node library project." 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/nx-release/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2020", 5 | "module": "ES2020", 6 | "outDir": "../../dist/out-tsc", 7 | "esModuleInterop": true, 8 | "declaration": true, 9 | "types": ["node"] 10 | }, 11 | "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], 12 | "include": ["**/*.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-form/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { EnvironmentName } from '@abgov/nx-oc'; 2 | import { FormDefinition } from '../../utils/form'; 3 | 4 | export interface Schema { 5 | project: string; 6 | env: EnvironmentName; 7 | accessToken?: string; 8 | } 9 | 10 | export interface NormalizedSchema extends Schema { 11 | projectRoot: string; 12 | formDefinition: FormDefinition; 13 | } 14 | -------------------------------------------------------------------------------- /packages/nx-oc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/nx-release/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /e2e/nx-oc-e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'nx-oc-e2e', 3 | preset: '../../jest.preset.js', 4 | globals: {}, 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsConfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/e2e/nx-oc-e2e', 15 | }; 16 | -------------------------------------------------------------------------------- /packages/nx-oc/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'nx-oc', 4 | preset: '../../jest.preset.js', 5 | globals: {}, 6 | transform: { 7 | '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 10 | coverageDirectory: '../../coverage/packages/nx-oc', 11 | testEnvironment: 'node', 12 | }; 13 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/files/.releaserc.json__tmpl__: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "<%= offsetFromRoot %>.releaserc.json", 3 | "tagFormat": "<%= project %>-v${version}", 4 | "plugins": [ 5 | ["@abgov/nx-release", { 6 | "project": "<%= project %>" 7 | }], 8 | ["@semantic-release/npm", { 9 | "pkgRoot": "<%= projectDist %>" 10 | }], 11 | "@semantic-release/github" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /e2e/nx-adsp-e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'nx-adsp-e2e', 3 | preset: '../../jest.preset.js', 4 | globals: {}, 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsConfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/e2e/nx-adsp-e2e', 15 | }; 16 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | pipeline: string; 3 | type: string; 4 | infra: string; 5 | envs: string; 6 | apply?: boolean; 7 | } 8 | 9 | export interface NormalizedSchema extends Schema { 10 | ocPipelineName: string; 11 | ocInfraProject: string; 12 | ocEnvProjects: string[]; 13 | applyPipeline: boolean; 14 | pipelineType: 'jenkins' | 'actions'; 15 | } 16 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /e2e/nx-release-e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'nx-release-e2e', 3 | preset: '../../jest.preset.js', 4 | globals: {}, 5 | transform: { 6 | '^.+\\.[tj]s$': [ 7 | 'ts-jest', 8 | { 9 | tsConfig: '/tsconfig.spec.json', 10 | }, 11 | ], 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/e2e/nx-release-e2e', 15 | }; 16 | -------------------------------------------------------------------------------- /packages/nx-release/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'nx-release', 4 | preset: '../../jest.preset.js', 5 | globals: {}, 6 | transform: { 7 | '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 10 | coverageDirectory: '../../coverage/packages/nx-release', 11 | testEnvironment: 'node', 12 | }; 13 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { EnvironmentName } from '@abgov/nx-oc'; 2 | import { QueueDefinition } from '../../utils/task'; 3 | 4 | export interface Schema { 5 | project: string; 6 | env: EnvironmentName; 7 | accessToken?: string; 8 | } 9 | 10 | export interface NormalizedSchema extends Schema { 11 | projectRoot: string; 12 | queueDefinition: QueueDefinition; 13 | updateStreamId: string; 14 | } 15 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/mern/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | 3 | export interface Schema { 4 | name: string; 5 | env: EnvironmentName; 6 | accessToken?: string; 7 | } 8 | 9 | export interface NormalizedSchema extends Schema { 10 | projectName: string; 11 | projectRoot: string; 12 | projectDirectory: string; 13 | openshiftDirectory: string; 14 | adsp: AdspConfiguration; 15 | } 16 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@abgov/semantic-release-nuget", 3 | "version": "0.0.0", 4 | "license": "Apache-2.0", 5 | "main": "src/index.js", 6 | "description": "Government of Alberta - Semantic release plugin for nuget.", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/GovAlta/nx-tools.git", 10 | "directory": "packages/semantic-release-nuget" 11 | }, 12 | "scripts": {} 13 | } -------------------------------------------------------------------------------- /packages/semantic-release-nuget/src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace SemanticRelease { 2 | import type { BaseContext } from 'semantic-release'; 3 | 4 | type PluginConfig = Record; 5 | 6 | type PluginFunction = ( 7 | pluginConfig: PluginConfig, 8 | context: T 9 | ) => Promise; 10 | } 11 | 12 | declare module '@semantic-release/semantic-release' { 13 | export = SemanticRelease; 14 | } 15 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-dotnet/schema.d.ts: -------------------------------------------------------------------------------- 1 | import type { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | 3 | export interface Schema { 4 | name: string; 5 | env: EnvironmentName; 6 | accessToken?: string; 7 | } 8 | 9 | export interface NormalizedSchema extends Schema { 10 | projectName: string; 11 | projectRoot: string; 12 | projectDirectory: string; 13 | openshiftDirectory: string; 14 | adsp: AdspConfiguration; 15 | } 16 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/react-oidc.d.ts__tmpl__: -------------------------------------------------------------------------------- 1 | // Declarations to allow redux-oidc to work with react 18 2 | declare module 'redux-oidc' { 3 | export interface CallbackComponentProps { 4 | children: React.ReactNode; 5 | } 6 | 7 | export interface OidcProviderProps { 8 | children: React.ReactNode; 9 | } 10 | 11 | export interface SignoutCallbackComponentProps { 12 | children: React.ReactNode; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.html__tmpl__: -------------------------------------------------------------------------------- 1 |

Redirected to auth-callback route!

2 |

Now you are logged in and allowed to access the "protected" route...

3 |
4 |
5 |
Token Type:
6 |
{{ tokenType }}
7 |
8 |
9 |
Token:
10 |
{{ accessToken }}
11 |
12 |
13 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/src/plugin/config.ts: -------------------------------------------------------------------------------- 1 | import { PluginConfig } from '@semantic-release/semantic-release'; 2 | 3 | export interface NugetPluginConfig extends PluginConfig { 4 | configuration: string; 5 | noBuild: boolean; 6 | project: string; 7 | includeSymbols: boolean; 8 | includeSource: boolean; 9 | serviceable: boolean; 10 | nupkgRoot: string; 11 | source: string; 12 | symbolSource: string; 13 | skipDuplicate: boolean; 14 | timeout: number; 15 | } 16 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/main.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /packages/nx-adsp/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.spec.ts", 10 | "**/*.test.ts", 11 | "**/*.spec.tsx", 12 | "**/*.test.tsx", 13 | "**/*.spec.js", 14 | "**/*.test.js", 15 | "**/*.spec.jsx", 16 | "**/*.test.jsx", 17 | "**/*.d.ts", 18 | "jest.config.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-oc/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.spec.ts", 10 | "**/*.test.ts", 11 | "**/*.spec.tsx", 12 | "**/*.test.tsx", 13 | "**/*.spec.js", 14 | "**/*.test.js", 15 | "**/*.spec.jsx", 16 | "**/*.test.jsx", 17 | "**/*.d.ts", 18 | "jest.config.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/files/src/environments/environment.prod.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | export const environment = { 5 | production: true, 6 | TENANT_REALM: '<%= tenantRealm %>', 7 | ACCESS_SERVICE_URL: '<%= accessServiceUrl %>', 8 | DIRECTORY_SERVICE_URL: '<%= directoryServiceUrl %>', 9 | CLIENT_ID: 'urn:ads:<%= tenant %>:<%= projectName %>', 10 | CLIENT_SECRET: '', 11 | LOG_LEVEL: 'info', 12 | ...process.env, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.spec.ts", 10 | "**/*.test.ts", 11 | "**/*.spec.tsx", 12 | "**/*.test.tsx", 13 | "**/*.spec.js", 14 | "**/*.test.js", 15 | "**/*.spec.jsx", 16 | "**/*.test.jsx", 17 | "**/*.d.ts", 18 | "jest.config.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | 18 | 19 |
20 |

404

21 | 22 |

Page not found :(

23 |

The requested page could not be found.

24 |
25 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/appsettings.Development.json__tmpl__: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "Adsp": { 9 | "AccessServiceUrl": "<%= accessServiceUrl %>", 10 | "Realm": "<%= tenantRealm %>", 11 | "DirectoryUrl": "<%= directoryServiceUrl %>", 12 | "ClientId": "urn:ads:<%= tenant %>:<%= projectName %>", 13 | "ClientSecret": "" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxRelease", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "project": { 8 | "type": "string", 9 | "description": "", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "Which project do you want to add semantic release to?" 15 | } 16 | }, 17 | "required": [ 18 | "project" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/files/src/environments/environment.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | export const environment = { 5 | production: false, 6 | port: '3333', 7 | TENANT_REALM: '<%= tenantRealm %>', 8 | ACCESS_SERVICE_URL: '<%= accessServiceUrl %>', 9 | DIRECTORY_SERVICE_URL: '<%= directoryServiceUrl %>', 10 | CLIENT_ID: 'urn:ads:<%= tenant %>:<%= projectName %>', 11 | CLIENT_SECRET: '', 12 | LOG_LEVEL: 'debug', 13 | ...process.env 14 | }; 15 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'semantic-release-nuget', 4 | preset: '../../jest.preset.js', 5 | globals: {}, 6 | testEnvironment: 'node', 7 | transform: { 8 | '^.+\\.[tj]sx?$': [ 9 | 'ts-jest', 10 | { 11 | tsconfig: '/tsconfig.spec.json', 12 | }, 13 | ], 14 | }, 15 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 16 | coverageDirectory: '../../coverage/packages/semantic-release-nuget', 17 | }; 18 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/appsettings.json__tmpl__: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "Adsp": { 9 | "AccessServiceUrl": "<%= accessServiceUrl %>", 10 | "Realm": "<%= tenantRealm %>", 11 | "DirectoryUrl": "<%= directoryServiceUrl %>", 12 | "ClientId": "urn:ads:<%= tenant %>:<%= projectName %>", 13 | "ClientSecret": "" 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /packages/nx-release/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "esModuleInterop": true, 7 | "types": ["jest", "node"] 8 | }, 9 | "include": [ 10 | "**/*.spec.ts", 11 | "**/*.test.ts", 12 | "**/*.spec.tsx", 13 | "**/*.test.tsx", 14 | "**/*.spec.js", 15 | "**/*.test.js", 16 | "**/*.spec.jsx", 17 | "**/*.test.jsx", 18 | "**/*.d.ts", 19 | "jest.config.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/nx-oc/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.releaserc.json", 3 | "tagFormat": "nx-oc-v${version}", 4 | "plugins": [ 5 | [ 6 | "@abgov/nx-release", 7 | { 8 | "project": "nx-oc" 9 | } 10 | ], 11 | [ 12 | "@semantic-release/npm", 13 | { 14 | "pkgRoot": "dist/packages/nx-oc" 15 | } 16 | ], 17 | [ 18 | "@semantic-release/github", 19 | { 20 | "releasedLabels": false, 21 | "successComment": false 22 | } 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/nx-adsp/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.releaserc.json", 3 | "tagFormat": "nx-adsp-v${version}", 4 | "plugins": [ 5 | [ 6 | "@abgov/nx-release", 7 | { 8 | "project": "nx-adsp" 9 | } 10 | ], 11 | [ 12 | "@semantic-release/npm", 13 | { 14 | "pkgRoot": "dist/packages/nx-adsp" 15 | } 16 | ], 17 | [ 18 | "@semantic-release/github", 19 | { 20 | "releasedLabels": false, 21 | "successComment": false 22 | } 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/environments/environment.ts__tmpl__: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // When building for production, this file is replaced with `environment.prod.ts`. 3 | 4 | export const environment = { 5 | production: false, 6 | directory: { 7 | url: '<%= directoryServiceUrl %>', 8 | }, 9 | access: { 10 | url: '<%= accessServiceUrl %>', 11 | realm: '<%= tenantRealm %>', 12 | client_id: 'urn:ads:<%= tenant %>:<%= projectName %>' 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /packages/nx-release/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.releaserc.json", 3 | "tagFormat": "nx-release-v${version}", 4 | "plugins": [ 5 | [ 6 | "@abgov/nx-release", 7 | { 8 | "project": "nx-release" 9 | } 10 | ], 11 | [ 12 | "@semantic-release/npm", 13 | { 14 | "pkgRoot": "dist/packages/nx-release" 15 | } 16 | ], 17 | [ 18 | "@semantic-release/github", 19 | { 20 | "releasedLabels": false, 21 | "successComment": false 22 | } 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/deployment/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { AdspConfiguration } from '../../adsp'; 2 | 3 | export type ApplicationType = 'node' | 'dotnet' | 'frontend'; 4 | 5 | export interface Schema { 6 | project: string; 7 | appType?: ApplicationType; 8 | env: EnvironmentName; 9 | adsp?: AdspConfiguration; 10 | accessToken?: string; 11 | } 12 | 13 | export interface NormalizedSchema extends Schema { 14 | projectName: string; 15 | appType: ApplicationType; 16 | ocInfraProject: string; 17 | ocEnvProjects: string[]; 18 | adsp: AdspConfiguration; 19 | } 20 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/schema.d.ts: -------------------------------------------------------------------------------- 1 | import type { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | 3 | export interface AngularAppGeneratorSchema { 4 | name: string; 5 | env: EnvironmentName; 6 | accessToken?: string; 7 | proxy?: NginxProxyConfiguration | NginxProxyConfiguration[]; 8 | } 9 | 10 | export interface NormalizedSchema extends AngularAppGeneratorSchema { 11 | projectName: string; 12 | projectRoot: string; 13 | openshiftDirectory: string; 14 | adsp: AdspConfiguration; 15 | nginxProxies: NginxProxyConfiguration[]; 16 | } 17 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/index.ts: -------------------------------------------------------------------------------- 1 | import type { AnalyzeCommitsContext, GenerateNotesContext } from 'semantic-release'; 2 | import { analyzeCommits as baseAnalyzeCommits } from '@semantic-release/commit-analyzer'; 3 | import { generateNotes as baseGenerateNotes } from '@semantic-release/release-notes-generator'; 4 | import { wrapPlugin } from './wrap-plugin.js'; 5 | 6 | const analyzeCommits = wrapPlugin(baseAnalyzeCommits); 7 | const generateNotes = wrapPlugin(baseGenerateNotes); 8 | 9 | export { analyzeCommits, generateNotes }; 10 | -------------------------------------------------------------------------------- /packages/nx-adsp/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'nx-adsp', 4 | preset: '../../jest.preset.js', 5 | globals: {}, 6 | transform: { 7 | '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], 8 | }, 9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], 10 | moduleNameMapper: { 11 | '@nx/angular/src/utils/test-runners': 12 | '/../../node_modules/@nx/angular/src/utils/test-runners.js', 13 | }, 14 | coverageDirectory: '../../coverage/packages/nx-adsp', 15 | testEnvironment: 'node', 16 | }; 17 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/utils/form.ts: -------------------------------------------------------------------------------- 1 | import { compile } from 'json-schema-to-typescript'; 2 | 3 | export interface FormDefinition { 4 | id: string; 5 | name: string; 6 | dataSchema: Record; 7 | assessorRoles: string[]; 8 | applicantRoles: string[]; 9 | } 10 | 11 | export async function generateFormInterface({ 12 | name, 13 | dataSchema, 14 | }: FormDefinition): Promise { 15 | const types = await compile(dataSchema, name, { 16 | additionalProperties: false, 17 | bannerComment: '', 18 | format: false, 19 | }); 20 | 21 | return types; 22 | } 23 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { AdspConfiguration, EnvironmentName } from '@abgov/nx-oc'; 2 | import { NginxProxyConfiguration } from '../../utils/nginx'; 3 | 4 | export interface Schema { 5 | name: string; 6 | env: EnvironmentName; 7 | accessToken?: string; 8 | proxy?: NginxProxyConfiguration | NginxProxyConfiguration[]; 9 | } 10 | 11 | export interface NormalizedSchema extends Schema { 12 | projectName: string; 13 | projectRoot: string; 14 | openshiftDirectory: string; 15 | adsp: AdspConfiguration; 16 | nginxProxies: NginxProxyConfiguration[]; 17 | } 18 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.releaserc.json", 3 | "tagFormat": "semantic-release-nuget-v${version}", 4 | "plugins": [ 5 | [ 6 | "@abgov/nx-release", 7 | { 8 | "project": "semantic-release-nuget" 9 | } 10 | ], 11 | [ 12 | "@semantic-release/npm", 13 | { 14 | "pkgRoot": "dist/packages/semantic-release-nuget" 15 | } 16 | ], 17 | [ 18 | "@semantic-release/github", 19 | { 20 | "releasedLabels": false, 21 | "successComment": false 22 | } 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/nx-oc/README.md: -------------------------------------------------------------------------------- 1 | # About this project 2 | This is the Government of Alberta - Nx plugin for OpenShift. 3 | 4 | The plugin includes generators for setting up OpenShift pipeline and application yaml files, and executors for `oc cli` commands. 5 | 6 | ## TLDR 7 | 8 | 1. Create your Nx workspace using `npx create-nx-workspace my-workspace` 9 | 2. Install plugin: `npm i -D @abgov/nx-oc` 10 | 3. Login to OpenShift: `oc login {url} --token={token}` 11 | 4. Generate basic infrastructure yml and apply it: `npx nx g @abgov/nx-oc:pipeline` 12 | 5. Set up OpenShift yml on apps using `npx nx g @abgov/nx-oc:deployment` 13 | 14 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/Examples/HelloWorldEvent.cs__tmpl__: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Adsp.Sdk.Examples; 4 | 5 | public class HelloWorldEvent 6 | { 7 | public const string EventName = "hello-world-event"; 8 | 9 | [JsonPropertyName("fromUserId")] 10 | public string? FromUserId { get; set; } 11 | 12 | [JsonPropertyName("fromUser")] 13 | public string? FromUser { get; set; } 14 | 15 | [JsonPropertyName("message")] 16 | public string? Message { get; set; } 17 | 18 | [JsonPropertyName("response")] 19 | public string? Response { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /e2e/nx-oc-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-oc-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "application", 5 | "sourceRoot": "e2e/nx-oc-e2e/src", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/jest:jest", 9 | "options": { 10 | "npmPackageName": "@abgov/nx-oc", 11 | "pluginOutputPath": "dist/packages/nx-oc", 12 | "jestConfig": "e2e/nx-oc-e2e/jest.config.js", 13 | "runInBand": true 14 | }, 15 | "dependsOn": ["nx-oc:build"] 16 | } 17 | }, 18 | "tags": [], 19 | "implicitDependencies": ["nx-oc"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/environments/environment.ts__tmpl__: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // When building for production, this file is replaced with `environment.prod.ts`. 3 | 4 | export const environment = { 5 | production: false, 6 | access: { 7 | url: '<%= accessServiceUrl %>', 8 | realm: '<%= tenantRealm %>', 9 | client_id: 'urn:ads:<%= tenant %>:<%= projectName %>' 10 | }, 11 | tenantApi: { 12 | host: "http://localhost:3333", 13 | endpoints: { 14 | tenantNameByRealm: "/api/tenant/v1/realm", 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /e2e/nx-adsp-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-adsp-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "application", 5 | "sourceRoot": "e2e/nx-adsp-e2e/src", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/jest:jest", 9 | "options": { 10 | "npmPackageName": "@abgov/nx-adsp", 11 | "pluginOutputPath": "dist/packages/nx-adsp", 12 | "jestConfig": "e2e/nx-adsp-e2e/jest.config.js", 13 | "runInBand": true 14 | }, 15 | "dependsOn": ["nx-adsp:build"] 16 | } 17 | }, 18 | "tags": [], 19 | "implicitDependencies": ["nx-adsp"] 20 | } 21 | -------------------------------------------------------------------------------- /e2e/nx-release-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-release-e2e", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "application", 5 | "sourceRoot": "e2e/nx-release-e2e/src", 6 | "targets": { 7 | "e2e": { 8 | "executor": "@nx/jest:jest", 9 | "options": { 10 | "npmPackageName": "@abgov/nx-release", 11 | "pluginOutputPath": "dist/packages/nx-release", 12 | "jestConfig": "e2e/nx-release-e2e/jest.config.js", 13 | "runInBand": true 14 | }, 15 | "dependsOn": ["nx-release:build"] 16 | } 17 | }, 18 | "tags": [], 19 | "implicitDependencies": ["nx-release"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/services/auth-guard.service.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate } from '@angular/router'; 3 | import { AuthService } from './auth.service'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class AuthGuardService implements CanActivate { 9 | 10 | constructor(private authService: AuthService) { } 11 | 12 | canActivate(): boolean { 13 | if (this.authService.isLoggedIn()) { 14 | return true; 15 | } 16 | 17 | console.log('start authentication...'); 18 | this.authService.startAuthentication(); 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/environments/config.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { environment } from './environment'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | @Injectable() 5 | export class Config { 6 | constructor() {} 7 | 8 | Init() { 9 | return new Promise((resolve, reject) => { 10 | console.log("AppInitService.init() called"); 11 | 12 | fetch('/config/config.json') 13 | .then((res) => { 14 | return (res.ok ? res.json() : environment) 15 | }).then(( envData ) => { 16 | localStorage.setItem('envData', JSON.stringify(envData)); 17 | resolve(); 18 | }) 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | .nx 4 | 5 | # compiled output 6 | /dist 7 | /tmp 8 | /out-tsc 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # IDEs and editors 14 | /.idea 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # IDE - VSCode 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # misc 30 | /.sass-cache 31 | /connect.lock 32 | /coverage 33 | /libpeerconnection.log 34 | npm-debug.log 35 | yarn-error.log 36 | testem.log 37 | /typings 38 | 39 | # System Files 40 | .DS_Store 41 | Thumbs.db 42 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | 3 | module.exports = { 4 | ...nxPreset, 5 | /* TODO: Update to latest Jest snapshotFormat 6 | * By default Nx has kept the older style of Jest Snapshot formats 7 | * to prevent breaking of any existing tests with snapshots. 8 | * It's recommend you update to the latest format. 9 | * You can do this by removing snapshotFormat property 10 | * and running tests with --update-snapshot flag. 11 | * Example: "nx affected --targets=e2e --update-snapshot" 12 | * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format 13 | */ 14 | snapshotFormat: { escapeString: true, printBasicPrototype: true }, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/README.md: -------------------------------------------------------------------------------- 1 | # About this project 2 | This is the Government of Alberta - Semantic Release plugin for Nuget. 3 | 4 | This plugin includes the following stages and executes: 5 | 6 | **prepare** 7 | 8 | `dotnet pack [options] /p:Version=[semVer]` 9 | 10 | Note that the semantic version is provided as the `Version` MSBuild property and no other version handling is provided (`VersionSuffix`, `AssemblyVersion`, etc.). 11 | 12 | **publish** 13 | 14 | `dotnet nuget push *.nupkg` 15 | 16 | The `nupkgRoot` configuration property is used to determine the working directory for the command. 17 | 18 | The environment variable `NUGET_API_KEY` is used as the API key for pushing to the Nuget source. 19 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/tenant.service.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export default class TenantService { 6 | 7 | private getTenantNameUrl = `${this.configData().tenantApi.host}${this.configData().tenantApi.endpoints.tenantNameByRealm}/${this.configData().access.realm}`; 8 | 9 | constructor(private http: HttpClient) {} 10 | 11 | /** GET tenants from the server */ 12 | getTenant() { 13 | return this.http.get(this.getTenantNameUrl); 14 | } 15 | 16 | configData () { 17 | return JSON.parse(localStorage.getItem('envData') || "\"\""); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/webpack.config.js__tmpl__: -------------------------------------------------------------------------------- 1 | const { composePlugins, withNx } = require('@nx/webpack'); 2 | const { withReact } = require('@nx/react'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | // Nx plugins for webpack. 6 | module.exports = composePlugins(withNx(), withReact(), (config) => { 7 | // Update the webpack config as needed here. 8 | // e.g. `config.plugins.push(new MyPlugin())` 9 | config.entry['renew'] = ['./src/renew.ts']; 10 | 11 | config.plugins.push( 12 | new HtmlWebpackPlugin({ 13 | template: './src/renew.html', 14 | chunks: ['renew'], 15 | filename: 'renew.html', 16 | }) 17 | ); 18 | 19 | return config; 20 | }); 21 | -------------------------------------------------------------------------------- /packages/nx-oc/src/utils/oc-utils.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | 3 | export function runOcCommand( 4 | command: 'project' | 'start-build' | 'process' | 'apply', 5 | params: string[], 6 | input?: Buffer 7 | ): { success: boolean, stdout?: Buffer} { 8 | const execute = !input ? 9 | `oc ${command} ${params.join(' ')}` : 10 | `cat | oc ${command} ${['-f -', ...params].join(' ')}`; 11 | 12 | try { 13 | console.log(`Executing command: ${execute}`); 14 | const stdout = execSync(execute, { stdio: 'pipe', input }); 15 | return { success: true, stdout }; 16 | } catch (e) { 17 | console.log(`Failed to execute command: ${execute}`, e); 18 | return { success: false }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/index.html__tmpl__: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= projectName %> 6 | 7 | 8 | 9 | 10 | 11 | 15 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /packages/nx-oc/src/adsp/environments.ts: -------------------------------------------------------------------------------- 1 | export type EnvironmentName = 'dev' | 'test' | 'prod'; 2 | interface Environment { 3 | accessServiceUrl: string; 4 | directoryServiceUrl: string; 5 | } 6 | 7 | export const environments: Record = { 8 | dev: { 9 | accessServiceUrl: 'https://access.adsp-dev.gov.ab.ca', 10 | directoryServiceUrl: 'https://directory-service.adsp-dev.gov.ab.ca', 11 | }, 12 | test: { 13 | accessServiceUrl: 'https://access-uat.alberta.ca', 14 | directoryServiceUrl: 'https://directory-service.adsp-uat.alberta.ca', 15 | }, 16 | prod: { 17 | accessServiceUrl: 'https://access.alberta.ca', 18 | directoryServiceUrl: 'https://directory-service.adsp.alberta.ca', 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/index.html__tmpl__: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= projectName %> 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | <<%= projectName %>-app-root>-app-root> 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/files/__fileName__/task.d.ts__tmpl__: -------------------------------------------------------------------------------- 1 | export interface QueueDefinition { 2 | namespace: string; 3 | name: string; 4 | description: string; 5 | assignerRoles: string[]; 6 | workerRoles: string[]; 7 | } 8 | 9 | export interface Task { 10 | id: string; 11 | name: string; 12 | description: string; 13 | priority: string; 14 | status: 'Pending' | 'In Progress' | 'Stopped' | 'Cancelled' | 'Completed'; 15 | createdOn: string; 16 | startedOn: string; 17 | endedOn: string; 18 | assignment: { 19 | assignedTo: { 20 | id: string; 21 | name: string; 22 | }; 23 | }; 24 | } 25 | 26 | export interface Person { 27 | id: string; 28 | name: string; 29 | email: string; 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.trimAutoWhitespace": true, 4 | "editor.tabSize": 2, 5 | "files.trimTrailingWhitespace": false, 6 | "[html]": { 7 | "editor.defaultFormatter": "esbenp.prettier-vscode" 8 | }, 9 | "[typescriptreact]": { 10 | "editor.defaultFormatter": "esbenp.prettier-vscode" 11 | }, 12 | "[javascript]": { 13 | "editor.defaultFormatter": "esbenp.prettier-vscode" 14 | }, 15 | "[jsonc]": { 16 | "editor.defaultFormatter": "vscode.json-language-features" 17 | }, 18 | "[typescript]": { 19 | "editor.defaultFormatter": "esbenp.prettier-vscode" 20 | }, 21 | "cSpell.words": [ 22 | "abgov", 23 | "Adsp", 24 | "nrwl", 25 | "openshift", 26 | "releaserc" 27 | ] 28 | } -------------------------------------------------------------------------------- /packages/nx-oc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@abgov/nx-oc", 3 | "version": "0.0.0", 4 | "license": "Apache-2.0", 5 | "main": "src/index.js", 6 | "description": "Government of Alberta - Nx plugin for OpenShift.", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/GovAlta/nx-tools.git", 10 | "directory": "packages/nx-oc" 11 | }, 12 | "peerDependencies": { 13 | "@nx/devkit": "^20.0.0", 14 | "tslib": "^2.0.0" 15 | }, 16 | "dependencies": { 17 | "axios": "^1.6.0", 18 | "enquirer": "^2.3.6", 19 | "express": "^4.18.2", 20 | "open": "^8.4.0", 21 | "simple-oauth2": "^5.0.0", 22 | "yaml": "~2.2.2" 23 | }, 24 | "generators": "./generators.json", 25 | "executors": "./executors.json", 26 | "scripts": {} 27 | } -------------------------------------------------------------------------------- /packages/nx-adsp/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "rules": { 5 | "@nx/dependency-checks": [ 6 | "error", 7 | { 8 | "ignoredDependencies": [ 9 | "@abgov/nx-oc" 10 | ], 11 | "checkMissingDependencies": true, // toggle to disable 12 | "checkObsoleteDependencies": true, // toggle to disable 13 | "checkVersionMismatches": true // toggle to disable 14 | } 15 | ] 16 | }, 17 | "overrides": [ 18 | { 19 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 20 | "rules": {} 21 | }, 22 | { 23 | "files": ["*.ts", "*.tsx"], 24 | "rules": {} 25 | }, 26 | { 27 | "files": ["*.js", "*.jsx"], 28 | "rules": {} 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-form/files/__fileName__/__fileName__.slice.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | initial<%= className %>State, 3 | <%= propertyName %>Actions, 4 | <%= propertyName %>Reducer, 5 | } from './<%= fileName %>.slice'; 6 | 7 | describe('<%= name %> slice', () => { 8 | describe('<%= propertyName %>Reducer', () => { 9 | it('can handle initial state', () => { 10 | expect(<%= propertyName %>Reducer(undefined, { type: '' })).toEqual( 11 | initial<%= className %>State 12 | ); 13 | }); 14 | 15 | it('can handle set step action', () => { 16 | expect( 17 | <%= propertyName %>Reducer(undefined, <%= propertyName %>Actions.setStep(2)) 18 | ).toMatchObject({ step: 2 }); 19 | }); 20 | 21 | // TODO: Add state unit tests. 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/nx-release/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@abgov/nx-release", 3 | "version": "0.0.0", 4 | "license": "Apache-2.0", 5 | "main": "src/index.js", 6 | "description": "Government of Alberta - Nx plugin for Semantic Release.", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/GovAlta/nx-tools.git", 10 | "directory": "packages/nx-release" 11 | }, 12 | "peerDependencies": { 13 | "@nx/devkit": "^20.0.0", 14 | "semantic-release": "^23.0.0", 15 | "tslib": "^2.0.0" 16 | }, 17 | "dependencies": { 18 | "@semantic-release/commit-analyzer": "^11.1.0", 19 | "@semantic-release/release-notes-generator": "^12.1.0", 20 | "get-stream": "~6.0.0", 21 | "split2": "~4.2.0" 22 | }, 23 | "generators": "./generators.json", 24 | "scripts": {} 25 | } 26 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/protected/protected.component.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import TenantService from '../tenant.service'; 3 | 4 | @Component({ 5 | selector: 'app-protected', 6 | templateUrl: './protected.component.html', 7 | styleUrls: ['./protected.component.css'], 8 | }) 9 | export class ProtectedComponent implements OnInit { 10 | constructor(private _tenantService: TenantService) {} 11 | 12 | public tenant = { name: '' }; 13 | 14 | getTenant() { 15 | this._tenantService.getTenant().subscribe( 16 | (data: any) => { 17 | this.tenant = data.tenant; 18 | }, 19 | (err) => console.error(err), 20 | () => console.log('done loading tenant') 21 | ); 22 | } 23 | 24 | ngOnInit() { 25 | this.getTenant(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/assets/github-1.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/nx-adsp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@abgov/nx-adsp", 3 | "version": "0.0.0", 4 | "license": "Apache-2.0", 5 | "main": "src/index.js", 6 | "description": "Government of Alberta - Nx plugin for ADSP apps.", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/GovAlta/nx-tools.git", 10 | "directory": "packages/nx-adsp" 11 | }, 12 | "peerDependencies": { 13 | "@abgov/nx-oc": "^10.0.0", 14 | "@nx-dotnet/core": "^2.5.0", 15 | "@nx/angular": "^20.0.0", 16 | "@nx/express": "^20.0.0", 17 | "@nx/devkit": "^20.0.0", 18 | "@nx/react": "^20.0.0", 19 | "@nx/eslint": "^20.0.0", 20 | "tslib": "^2.0.0" 21 | }, 22 | "dependencies": { 23 | "axios": "^1.6.0", 24 | "enquirer": "^2.3.6", 25 | "json-schema-to-typescript": "^13.0.1" 26 | }, 27 | "generators": "./generators.json", 28 | "scripts": {} 29 | } -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "target": "es2015", 12 | "module": "esnext", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "@abgov/nx-adsp": ["packages/nx-adsp/src/index.ts"], 19 | "@abgov/nx-oc": ["packages/nx-oc/src/index.ts"], 20 | "@abgov/nx-release": ["packages/nx-release/src/index.ts"], 21 | "@abgov/semantic-release-nuget": [ 22 | "packages/semantic-release-nuget/src/index.ts" 23 | ] 24 | } 25 | }, 26 | "exclude": ["node_modules", "tmp"] 27 | } 28 | -------------------------------------------------------------------------------- /e2e/nx-release-e2e/tests/nx-release.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | checkFilesExist, 3 | copyNodeModules, 4 | ensureNxProject, 5 | readJson, 6 | runNxCommandAsync, 7 | uniq, 8 | } from '@nx/plugin/testing'; 9 | describe('nx-release e2e', () => { 10 | beforeEach(() => { 11 | ensureNxProject('@abgov/nx-release', 'dist/packages/nx-release'); 12 | copyNodeModules(['@nx/node']); 13 | }); 14 | 15 | describe('lib', () => { 16 | it('should create release', async (done) => { 17 | const plugin = uniq('lib'); 18 | await runNxCommandAsync( 19 | `generate @nx/node:library ${plugin} --publishable --importPath @test/${plugin}` 20 | ); 21 | const result = await runNxCommandAsync( 22 | `generate @abgov/nx-release:lib ${plugin}` 23 | ); 24 | 25 | expect(result.stderr).toBeFalsy(); 26 | 27 | done(); 28 | }, 60000); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/nx-oc/generators.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "name": "nx-oc", 4 | "version": "0.0.1", 5 | "generators": { 6 | "pipeline": { 7 | "factory": "./src/generators/pipeline/pipeline", 8 | "schema": "./src/generators/pipeline/schema.json", 9 | "description": "Generator that creates OpenShift manifests for pipeline." 10 | }, 11 | "apply-infra": { 12 | "factory": "./src/generators/apply-infra/apply-infra", 13 | "schema": "./src/generators/apply-infra/schema.json", 14 | "description": "Generator that applies OpenShift manifests for pipeline." 15 | }, 16 | "deployment": { 17 | "factory": "./src/generators/deployment/deployment", 18 | "schema": "./src/generators/deployment/schema.json", 19 | "description": "Generator that creates OpenShift manifests for project deployment." 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/access.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { createUserManager as createOidcUserManager } from 'redux-oidc'; 2 | 3 | interface CreateUserManagerProps { 4 | url: string; 5 | realm: string; 6 | client_id: string; 7 | } 8 | 9 | export function createUserManager({ 10 | url, 11 | realm, 12 | client_id 13 | }: CreateUserManagerProps) { 14 | 15 | const appUrl = `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}`; 16 | const settings = { 17 | client_id, 18 | redirect_uri: `${appUrl}/auth/callback`, 19 | post_logout_redirect_uri: `${appUrl}/signout/callback`, 20 | silent_redirect_uri: `${appUrl}/renew.html`, 21 | response_type: 'code', 22 | authority: `${url}/auth/realms/${realm}`, 23 | automaticSilentRenew: true, 24 | }; 25 | return createOidcUserManager(settings); 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About the project 2 | This is a monorepo of the Government of Alberta's custom plugins for [Nx](https://nx.dev) 3 | 4 | [NX Tools Guide](https://govalta.github.io/nx-tools/getting-started.html) 5 | 6 | ## Plugins 7 | 8 | [Nx OpenShift Plugin](./packages/nx-oc/README.md) - includes generators for setting up OpenShift pipeline and application yaml files, and executors for `oc cli` commands. 9 | 10 | [Nx Release Plugin](./packages/nx-release/README.md) - includes generators for adding [semantic-release](https://github.com/semantic-release/semantic-release) based release targets. 11 | 12 | [Nx ADSP Plugin](./packages/nx-adsp/README.md) - includes generators for application, service, and fullstack solution quick starts. 13 | 14 | [Semantic Release Nuget](./packages/semantic-release-nuget/README.md) - [semantic-release](https://github.com/semantic-release/semantic-release) plugin for publishing nuget packages. 15 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/files/__fileName__/__fileName__.slice.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { 2 | initial<%= className %>TaskListState, 3 | <%= propertyName %>TaskListActions, 4 | <%= propertyName %>TaskListReducer, 5 | } from './<%= fileName %>.slice'; 6 | 7 | describe('<%= fileName %> slice', () => { 8 | describe('<%= propertyName %>TaskListReducer', () => { 9 | it('can handle initial state', () => { 10 | expect(<%= propertyName %>TaskListReducer(undefined, { type: '' })).toEqual( 11 | initial<%= className %>TaskListState 12 | ); 13 | }); 14 | 15 | it('can handle set step action', () => { 16 | expect( 17 | <%= propertyName %>TaskListReducer( 18 | undefined, 19 | <%= propertyName %>TaskListActions.setFilter('active') 20 | ) 21 | ).toMatchObject({ filter: 'active' }); 22 | }); 23 | 24 | // TODO: Add state unit tests. 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/nginx.conf__tmpl__: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | http{ 5 | sendfile on; 6 | include mime.types; 7 | default_type application/octet-stream; 8 | 9 | server { 10 | 11 | listen 8080; 12 | root /opt/app-root/src; 13 | index index.html; 14 | 15 | 16 | location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { 17 | expires 30d; 18 | add_header Cache-Control "public, no-transform"; 19 | } 20 | 21 | location / { 22 | gzip on; 23 | try_files $uri /index.html; 24 | } 25 | <% nginxProxies.forEach(function(nginxProxy){ %> 26 | location <%= nginxProxy.location %> { 27 | proxy_pass <%= nginxProxy.proxyPass %>; 28 | proxy_set_header Host $host; 29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 30 | proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; 31 | } 32 | <% }); %> 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/nginx.conf__tmpl__: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | http{ 5 | sendfile on; 6 | include mime.types; 7 | default_type application/octet-stream; 8 | 9 | server { 10 | 11 | listen 8080; 12 | root /opt/app-root/src; 13 | index index.html; 14 | 15 | 16 | location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { 17 | expires 30d; 18 | add_header Cache-Control "public, no-transform"; 19 | } 20 | 21 | location / { 22 | gzip on; 23 | try_files $uri /index.html; 24 | } 25 | <% nginxProxies.forEach(function(nginxProxy){ %> 26 | location <%= nginxProxy.location %> { 27 | proxy_pass <%= nginxProxy.proxyPass %>; 28 | proxy_set_header Host $host; 29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 30 | proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; 31 | } 32 | <% }); %> 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/git-utils.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'child_process'; 2 | import { array } from 'get-stream'; 3 | import split from 'split2'; 4 | 5 | const projectCommits: Record = {}; 6 | 7 | export async function getPathCommitHashes( 8 | cwd: string, 9 | project: string, 10 | paths: string[], 11 | from: string, 12 | to = 'HEAD' 13 | ) { 14 | if (!projectCommits[project]) { 15 | // --full-history to avoid history simplification ignoring commits on some merged branches. 16 | const commits = await array( 17 | spawn( 18 | 'git', 19 | [ 20 | 'log', 21 | '--format=%H', 22 | '--full-history', 23 | `${from ? `${from}..` : ''}${to}`, 24 | '--', 25 | ...paths, 26 | ], 27 | { cwd } 28 | ).stdout.pipe(split()) 29 | ); 30 | 31 | projectCommits[project] = commits; 32 | } 33 | 34 | return projectCommits[project]; 35 | } 36 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/home/home.component.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | @Component({ 5 | selector: '<%= projectName %>-app-logout', 6 | templateUrl: 'home.component.html', 7 | }) 8 | export class HomeComponent implements OnInit { 9 | constructor(private http: HttpClient) {} 10 | public uptime = 0; 11 | 12 | private getHealthUrl = `${this.configData().tenantApi.host}/health`; 13 | 14 | getHealth() { 15 | const uptimePromise = this.http.get(this.getHealthUrl); 16 | 17 | uptimePromise.subscribe( 18 | (data: any) => { 19 | this.uptime = data.uptime; 20 | }, 21 | (err) => console.error(err), 22 | () => console.log('done loading uptime') 23 | ); 24 | } 25 | 26 | configData () { 27 | return JSON.parse(localStorage.getItem('envData') || "\"\""); 28 | } 29 | 30 | ngOnInit() { 31 | this.getHealth(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.component.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from '@angular/core'; 2 | import { AuthService } from './services/auth.service'; 3 | import { environment } from '../environments/environment' 4 | 5 | 6 | @Component({ 7 | selector: '<%= projectName %>-app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'], 10 | }) 11 | 12 | export class AppComponent implements OnInit, OnDestroy { 13 | title = "<%= projectName %>"; 14 | 15 | constructor(private authService: AuthService) {} 16 | 17 | isLoggedIn(): boolean { 18 | return this.authService.isLoggedIn() 19 | } 20 | 21 | logout() { 22 | if (this.authService.isLoggedIn()) { 23 | this.authService.logout(); 24 | } 25 | } 26 | 27 | login() { 28 | if (!this.authService.isLoggedIn()) { 29 | this.authService.startAuthentication(); 30 | } 31 | } 32 | 33 | ngOnInit() {} 34 | 35 | ngOnDestroy(): void {} 36 | } 37 | -------------------------------------------------------------------------------- /packages/nx-oc/src/executors/apply/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "outputCapture": "direct-nodejs", 4 | "$schema": "http://json-schema.org/schema", 5 | "title": "Apply executor", 6 | "description": "", 7 | "type": "object", 8 | "properties": { 9 | "ocProject": { 10 | "oneOf": [ 11 | { 12 | "type": "string" 13 | }, 14 | { 15 | "type": "array", 16 | "items": { 17 | "oneOf": [ 18 | { 19 | "type": "string" 20 | }, 21 | { 22 | "type": "object", 23 | "properties": { 24 | "project": { 25 | "type": "string" 26 | }, 27 | "tag": { 28 | "type": "string" 29 | } 30 | } 31 | } 32 | ] 33 | } 34 | } 35 | ] 36 | } 37 | }, 38 | "required": [ 39 | "ocProject" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/apply-infra/apply-infra.ts: -------------------------------------------------------------------------------- 1 | import { Tree } from '@nx/devkit'; 2 | import { runOcCommand } from '../../utils/oc-utils'; 3 | 4 | function applyOcResources(host: Tree) { 5 | const { success: pipelineApplied, stdout: pipelineOut } = runOcCommand( 6 | 'apply', 7 | [], 8 | host.read('.openshift/environment.infra.yml') 9 | ); 10 | console.log(pipelineOut?.toString()); 11 | 12 | const { success: envApplied, stdout: envOut } = runOcCommand( 13 | 'apply', 14 | [], 15 | host.read('.openshift/environments.yml') 16 | ); 17 | console.log(envOut?.toString()); 18 | 19 | if (!pipelineApplied || !envApplied) { 20 | console.log('Failed to oc apply pipeline and dev environment.'); 21 | } 22 | } 23 | 24 | export default async function (host: Tree) { 25 | const { success } = runOcCommand('project', []); 26 | if (success) { 27 | applyOcResources(host); 28 | } else { 29 | console.log(`Use oc login then run 'nx g @abgov/nx-oc:apply-infra'`); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/auth.interceptor.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { AuthCallbackComponent } from './auth-callback/auth-callback.component'; 5 | 6 | @Injectable({ providedIn: 'root' }) 7 | export class AuthInterceptor implements HttpInterceptor { 8 | 9 | constructor(private authCallBackComponent: AuthCallbackComponent) { } 10 | 11 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 12 | if (this.authCallBackComponent.setTokens()) { 13 | req = req.clone({ 14 | setHeaders: { 15 | 'Content-Type' : 'application/json; charset=utf-8', 16 | 'Accept' : 'application/json', 17 | 'Authorization': `Bearer ${this.authCallBackComponent.accessToken}`, 18 | }, 19 | }); 20 | } 21 | 22 | return next.handle(req); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/apply-infra/apply-infra.spec.ts: -------------------------------------------------------------------------------- 1 | import { createTree } from '@nx/devkit/testing'; 2 | import { mocked } from 'jest-mock'; 3 | import generator from './apply-infra'; 4 | import { runOcCommand } from '../../utils/oc-utils'; 5 | import { writeJson } from '@nx/devkit'; 6 | 7 | jest.mock('../../utils/oc-utils'); 8 | const mockedRunOcCommand = mocked(runOcCommand); 9 | 10 | describe('Apply Infra Generator', () => { 11 | beforeEach(() => { 12 | mockedRunOcCommand.mockReset(); 13 | }); 14 | 15 | it('can run', async () => { 16 | const host = createTree(); 17 | writeJson(host, '.openshift/environment.infra.yml', {}); 18 | writeJson(host, '.openshift/environment.env.yml', {}); 19 | 20 | mockedRunOcCommand.mockReturnValue({ success: true }); 21 | 22 | await generator(host); 23 | 24 | expect(mockedRunOcCommand.mock.calls.length).toBe(3); 25 | expect(mockedRunOcCommand.mock.calls[0][0]).toBe('project'); 26 | expect(mockedRunOcCommand.mock.calls[1][0]).toBe('apply'); 27 | expect(mockedRunOcCommand.mock.calls[2][0]).toBe('apply'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/jenkins/environments.yml__tmpl__: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: rbac.authorization.k8s.io/v1 5 | kind: RoleBinding 6 | metadata: 7 | name: env_deploy_pullers 8 | namespace: <%= ocInfraProject %> 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: 'system:image-puller' 13 | subjects: 14 | <% ocEnvProjects.forEach(function(ocEnvProject){ -%> 15 | - apiGroup: rbac.authorization.k8s.io 16 | kind: Group 17 | name: 'system:serviceaccounts:<%= ocEnvProject %>' 18 | <% }); -%> 19 | <% ocEnvProjects.forEach(function(ocEnvProject){ -%> 20 | - apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: jenkins_edit 24 | namespace: <%= ocEnvProject %> 25 | roleRef: 26 | apiGroup: rbac.authorization.k8s.io 27 | kind: ClusterRole 28 | name: edit 29 | subjects: 30 | - kind: ServiceAccount 31 | name: jenkins 32 | namespace: <%= ocInfraProject %> 33 | <% }); -%> 34 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.routes.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { LogoutComponent } from './logout/logout.component'; 2 | import { NgModule } from '@angular/core'; 3 | import { Routes, RouterModule } from '@angular/router'; 4 | import { HomeComponent } from './home/home.component'; 5 | import { ProtectedComponent } from './protected/protected.component'; 6 | import { AuthGuardService } from './services/auth-guard.service'; 7 | import { AuthCallbackComponent } from './auth-callback/auth-callback.component'; 8 | 9 | const routes: Routes = [ 10 | { 11 | path: '', 12 | component: HomeComponent, 13 | children: [], 14 | }, 15 | { 16 | path: 'protected', 17 | component: ProtectedComponent, 18 | canActivate: [AuthGuardService], 19 | }, 20 | { 21 | path: 'auth-callback', 22 | component: AuthCallbackComponent, 23 | }, 24 | { 25 | path: 'signout/callback', 26 | component: LogoutComponent, 27 | }, 28 | ]; 29 | 30 | @NgModule({ 31 | imports: [RouterModule.forRoot(routes)], 32 | exports: [RouterModule], 33 | }) 34 | export class AppRoutingModule {} 35 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/git-utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { ChildProcess, spawn } from 'child_process'; 2 | import { ReadableStreamBuffer } from 'stream-buffers'; 3 | import { mocked } from 'jest-mock' 4 | import { getPathCommitHashes } from './git-utils'; 5 | 6 | jest.mock('child_process'); 7 | const mockedSpawn = mocked(spawn); 8 | 9 | describe('git-utils', () => { 10 | 11 | beforeEach(() => { 12 | mockedSpawn.mockReset() 13 | }); 14 | 15 | describe('getPathCommitHashes', () => { 16 | 17 | it('can get commits', async () => { 18 | 19 | const stream = new ReadableStreamBuffer(); 20 | stream.put('test1\ntest2\n'); 21 | stream.stop(); 22 | 23 | mockedSpawn.mockReturnValue({ stdout: stream } as unknown as ChildProcess) 24 | const commits = await getPathCommitHashes( 25 | '/repo', 26 | 'test', 27 | ['test'], 28 | '', 29 | 'HEAD' 30 | ); 31 | 32 | expect(commits.length).toBe(2); 33 | expect(commits[0]).toBe('test1'); 34 | expect(commits[1]).toBe('test2'); 35 | expect(mockedSpawn.mock.calls.length).toBe(1); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/mern/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspMern", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the MERN stack solution.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "name", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/types.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace SemanticRelease { 2 | import type { BaseContext } from 'semantic-release'; 3 | 4 | type PluginConfig = Record; 5 | 6 | type PluginFunction = ( 7 | pluginConfig: PluginConfig, 8 | context: T 9 | ) => Promise; 10 | } 11 | 12 | declare module '@semantic-release/semantic-release' { 13 | export = SemanticRelease; 14 | } 15 | 16 | declare namespace CommitAnalyzer { 17 | type ReleaseType = string; 18 | 19 | const analyzeCommits: SemanticRelease.PluginFunction; 20 | 21 | module.exports = { analyzeCommits }; 22 | } 23 | 24 | declare module '@semantic-release/commit-analyzer' { 25 | export = CommitAnalyzer; 26 | } 27 | 28 | declare module '@semantic-release/semantic-release' { 29 | export = SemanticRelease; 30 | } 31 | 32 | declare namespace ReleaseNotesGenerator { 33 | type ReleaseType = string; 34 | 35 | const generateNotes: SemanticRelease.PluginFunction; 36 | 37 | module.exports = { generateNotes }; 38 | } 39 | 40 | declare module '@semantic-release/release-notes-generator' { 41 | export = ReleaseNotesGenerator; 42 | } 43 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspDotnetService", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the service.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "name", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspExpressService", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the service.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "name", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-dotnet/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspReactDotnet", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the React-Dotnet stack solution.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "name", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-form/files/__fileName__/__fileName__.module.css__tmpl__: -------------------------------------------------------------------------------- 1 | .form { 2 | max-width: 800px; 3 | 4 | > fieldset { 5 | display: none; 6 | padding-bottom: 64px; 7 | } 8 | 9 | > fieldset.show { 10 | display: block; 11 | } 12 | } 13 | 14 | .formFile { 15 | display: flex; 16 | align-items: center; 17 | 18 | > a { 19 | display: none; 20 | } 21 | } 22 | 23 | .formActions { 24 | display: flex; 25 | margin-top: 16px; 26 | 27 | > * { 28 | margin-left: 16px; 29 | } 30 | 31 | .save { 32 | margin-right: auto; 33 | display: flex; 34 | opacity: 0; 35 | transition-property: opacity; 36 | transition-duration: 500ms; 37 | 38 | > * { 39 | margin-top: auto; 40 | margin-bottom: auto; 41 | margin-right: 8px; 42 | } 43 | } 44 | 45 | .save[data-show='true'] { 46 | opacity: 1; 47 | } 48 | } 49 | 50 | .sectionActions { 51 | display: flex; 52 | 53 | > *:first-child { 54 | margin-right: auto; 55 | } 56 | } 57 | 58 | .load { 59 | display: flex; 60 | flex-direction: row; 61 | padding-top: 64px; 62 | padding-bottom: 64px; 63 | > * { 64 | margin: auto; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/express-service.spec.ts: -------------------------------------------------------------------------------- 1 | import { readProjectConfiguration } from '@nx/devkit'; 2 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 3 | 4 | import * as utils from '@abgov/nx-oc'; 5 | import { environments } from '@abgov/nx-oc'; 6 | import { Schema } from './schema'; 7 | import generator from './express-service'; 8 | 9 | jest.mock('@abgov/nx-oc'); 10 | const utilsMock = utils as jest.Mocked; 11 | utilsMock.getAdspConfiguration.mockResolvedValue({ 12 | tenant: 'test', 13 | tenantRealm: 'test', 14 | accessServiceUrl: environments.test.accessServiceUrl, 15 | directoryServiceUrl: environments.test.directoryServiceUrl, 16 | }); 17 | 18 | describe('Express Service Generator', () => { 19 | const options: Schema = { 20 | name: 'test', 21 | env: 'dev', 22 | }; 23 | 24 | it('can run', async () => { 25 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 26 | await generator(host, options); 27 | 28 | const config = readProjectConfiguration(host, 'test'); 29 | expect(config.root).toBe('apps/test'); 30 | 31 | expect(host.exists('apps/test/src/main.ts')).toBeTruthy(); 32 | }, 60000); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-form/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspReactForm", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "project": { 8 | "type": "string", 9 | "description": "Project to add form component in.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "Which project to add form component for?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target? (Typically test for ADSP tenants.)", 26 | "type": "list", 27 | "items": [ 28 | "test", 29 | "prod", 30 | "dev" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "project", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspReactTaskList", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "project": { 8 | "type": "string", 9 | "description": "Project to add task list component in.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "Which project to add task list component for?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target? (Typically test for ADSP tenants.)", 26 | "type": "list", 27 | "items": [ 28 | "test", 29 | "prod", 30 | "dev" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | } 39 | }, 40 | "required": [ 41 | "project", 42 | "env" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["**/*"], 4 | "plugins": ["@nx"], 5 | "overrides": [ 6 | { 7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 8 | "rules": { 9 | "@nx/enforce-module-boundaries": [ 10 | "error", 11 | { 12 | "enforceBuildableLibDependency": true, 13 | "allow": [], 14 | "depConstraints": [ 15 | { 16 | "sourceTag": "*", 17 | "onlyDependOnLibsWithTags": ["*"] 18 | } 19 | ] 20 | } 21 | ] 22 | } 23 | }, 24 | { 25 | "files": ["*.ts", "*.tsx"], 26 | "extends": ["plugin:@nx/typescript"], 27 | "rules": { 28 | "@typescript-eslint/no-extra-semi": "error", 29 | "no-extra-semi": "off" 30 | } 31 | }, 32 | { 33 | "files": ["*.js", "*.jsx"], 34 | "extends": ["plugin:@nx/javascript"], 35 | "rules": { 36 | "@typescript-eslint/no-extra-semi": "error", 37 | "no-extra-semi": "off" 38 | } 39 | }, 40 | { 41 | "files": ["*.json"], 42 | "parser": "jsonc-eslint-parser", 43 | "rules": { 44 | "@nx/dependency-checks": "error" 45 | } 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/utils/form.spec.ts: -------------------------------------------------------------------------------- 1 | import { FormDefinition, generateFormInterface } from './form'; 2 | 3 | describe('generateFormInterface', () => { 4 | const dataSchema = { 5 | type: 'object', 6 | properties: { 7 | firstName: { 8 | type: 'string', 9 | }, 10 | lastName: { 11 | type: 'string', 12 | }, 13 | age: { 14 | description: 'Age in years', 15 | type: 'integer', 16 | minimum: 0, 17 | }, 18 | hairColor: { 19 | enum: ['black', 'brown', 'blue'], 20 | type: 'string', 21 | }, 22 | personal: { 23 | type: 'object', 24 | properties: { 25 | firstName: { 26 | type: 'string', 27 | }, 28 | lastName: { 29 | type: 'string', 30 | }, 31 | }, 32 | }, 33 | }, 34 | additionalProperties: false, 35 | required: ['firstName', 'lastName'], 36 | }; 37 | 38 | it('can generate form interface', async () => { 39 | const result = await generateFormInterface({ 40 | name: 'Test questionnaire', 41 | dataSchema: dataSchema as unknown, 42 | } as FormDefinition); 43 | 44 | expect(result).toBeTruthy(); 45 | // expect(result).toMatchSnapshot(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/src/plugin/prepare.ts: -------------------------------------------------------------------------------- 1 | import { PluginFunction } from '@semantic-release/semantic-release'; 2 | import { PrepareContext } from 'semantic-release'; 3 | import { exec as execCb } from 'child_process'; 4 | import { promisify } from 'util'; 5 | import { NugetPluginConfig } from './config'; 6 | const exec = promisify(execCb); 7 | 8 | export const prepare: PluginFunction = async ( 9 | config: NugetPluginConfig, 10 | context 11 | ) => { 12 | const { project, configuration, noBuild, includeSymbols, includeSource, serviceable } = 13 | config; 14 | 15 | const { 16 | cwd, 17 | env, 18 | // stdout, 19 | // stderr, 20 | nextRelease: { version }, 21 | } = context; 22 | 23 | const args = [ 24 | 'dotnet', 25 | 'pack', 26 | project, 27 | noBuild ? '--no-build' : null, 28 | includeSymbols ? '--include-symbols' : null, 29 | includeSource ? '--include-source' : null, 30 | serviceable ? '--serviceable' : null, 31 | `/p:Configuration=${configuration || 'Release'}`, 32 | `/p:Version=${version}`, 33 | ].filter((v) => !!v); 34 | 35 | const dotnetResult = await exec(args.join(' '), { env, cwd }); 36 | 37 | // dotnetResult.stdout.pipe(stdout, { end: false }); 38 | // dotnetResult.stderr.pipe(stderr, { end: false }); 39 | 40 | await dotnetResult; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/store.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { 3 | reducer as oidcReducer, 4 | LOAD_USER_ERROR, 5 | LOADING_USER, 6 | USER_EXPIRED, 7 | USER_EXPIRING, 8 | USER_FOUND, 9 | USER_LOADED, 10 | USER_SIGNED_OUT, 11 | } from 'redux-oidc'; 12 | import { CONFIG_FEATURE_KEY, configReducer } from './app/config.slice'; 13 | import { START_FEATURE_KEY, startReducer } from './app/start.slice'; 14 | import { INTAKE_FEATURE_KEY, intakeReducer } from './app/intake.slice'; 15 | 16 | export const store = configureStore({ 17 | reducer: { 18 | user: oidcReducer, 19 | [CONFIG_FEATURE_KEY]: configReducer, 20 | [START_FEATURE_KEY]: startReducer, 21 | [INTAKE_FEATURE_KEY]: intakeReducer, 22 | }, 23 | devTools: process.env.NODE_ENV !== 'production', 24 | // Optional Redux store enhancers 25 | enhancers: [], 26 | middleware: (getDefault) => 27 | getDefault({ 28 | serializableCheck: { 29 | ignoredActions: [ 30 | LOAD_USER_ERROR, 31 | LOADING_USER, 32 | USER_EXPIRED, 33 | USER_EXPIRING, 34 | USER_FOUND, 35 | USER_LOADED, 36 | USER_SIGNED_OUT, 37 | ], 38 | ignoredPaths: ['user'], 39 | }, 40 | }), 41 | }); 42 | export type AppDispatch = typeof store.dispatch; 43 | -------------------------------------------------------------------------------- /e2e/nx-oc-e2e/tests/nx-oc.spec.ts: -------------------------------------------------------------------------------- 1 | import { ensureNxProject, runNxCommandAsync, uniq } from '@nx/plugin/testing'; 2 | describe('nx-oc e2e', () => { 3 | beforeEach(() => ensureNxProject('@abgov/nx-oc', 'dist/packages/nx-oc')); 4 | 5 | describe('pipeline', () => { 6 | it('should create jenkins pipeline', async (done) => { 7 | const plugin = uniq('pipeline'); 8 | const result = await runNxCommandAsync( 9 | `generate @abgov/nx-oc:pipeline ${plugin}-build ${plugin}-infra --t jenkins --e ${plugin}-dev` 10 | ); 11 | 12 | expect(result.stderr).toBeFalsy(); 13 | 14 | done(); 15 | }); 16 | 17 | it('should create github actions pipeline', async (done) => { 18 | const plugin = uniq('pipeline'); 19 | const result = await runNxCommandAsync( 20 | `generate @abgov/nx-oc:pipeline ${plugin}-build ${plugin}-infra --t actions --e ${plugin}-dev` 21 | ); 22 | 23 | expect(result.stderr).toBeFalsy(); 24 | 25 | done(); 26 | }); 27 | }); 28 | 29 | describe('apply-infra', () => { 30 | it('should run oc cli', async (done) => { 31 | const plugin = uniq('apply-infra'); 32 | const result = await runNxCommandAsync( 33 | `generate @abgov/nx-oc:apply-infra` 34 | ); 35 | 36 | expect(result.stdout).toContain('oc project'); 37 | 38 | done(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspaceLayout": { 3 | "appsDir": "e2e", 4 | "libsDir": "packages" 5 | }, 6 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 7 | "targetDefaults": { 8 | "build": { 9 | "dependsOn": ["^build"], 10 | "inputs": ["production", "^production"], 11 | "cache": true 12 | }, 13 | "test": { 14 | "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], 15 | "cache": true 16 | }, 17 | "lint": { 18 | "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], 19 | "cache": true 20 | }, 21 | "e2e": { 22 | "cache": true 23 | } 24 | }, 25 | "namedInputs": { 26 | "default": ["{projectRoot}/**/*", "sharedGlobals"], 27 | "sharedGlobals": [ 28 | "{workspaceRoot}/workspace.json", 29 | "{workspaceRoot}/tsconfig.base.json", 30 | "{workspaceRoot}/tslint.json", 31 | "{workspaceRoot}/.github/**/*.yml", 32 | "{workspaceRoot}/nx.json" 33 | ], 34 | "production": [ 35 | "default", 36 | "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", 37 | "!{projectRoot}/tsconfig.spec.json", 38 | "!{projectRoot}/jest.config.[jt]s", 39 | "!{projectRoot}/.eslintrc.json", 40 | "!{projectRoot}/src/test-setup.[jt]s" 41 | ] 42 | }, 43 | "parallel": 1, 44 | "defaultBase": "origin/main", 45 | "useLegacyCache": true 46 | } 47 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { AuthCallbackComponent } from './auth-callback.component'; 3 | import { 4 | BrowserDynamicTestingModule, 5 | platformBrowserDynamicTesting 6 | } from "@angular/platform-browser-dynamic/testing"; 7 | 8 | describe('AuthCallbackComponent', () => { 9 | let component: AuthCallbackComponent; 10 | let fixture: ComponentFixture; 11 | let envData = {"production":false,"access":{"url":"https://testurl.com","realm":"123","client_id":"urn:ads:platform:tenant-admin-app"},"tenantApi":{"host":"http://localhost:3333","endpoints":{"tenantNameByRealm":"/api/tenant/v1/realm"}}} 12 | 13 | localStorage.setItem('envData', JSON.stringify(envData)); 14 | 15 | beforeEach((() => { 16 | TestBed.initTestEnvironment( 17 | BrowserDynamicTestingModule, 18 | platformBrowserDynamicTesting() 19 | ); 20 | 21 | TestBed.configureTestingModule({ 22 | declarations: [AuthCallbackComponent], 23 | }).compileComponents(); 24 | 25 | fixture = TestBed.createComponent(AuthCallbackComponent); 26 | component = fixture.componentInstance; 27 | fixture.detectChanges(); 28 | })); 29 | 30 | it('should create', () => { 31 | expect(component).toBeTruthy(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/nx-utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { getProjectChangePaths } from './nx-util'; 2 | jest.mock('child_process', () => ({ 3 | exec: jest.fn((_c, cb) => cb(null)), 4 | })); 5 | jest.mock('fs', () => ({ 6 | readFile: jest.fn((_f, _e, cb) => 7 | cb( 8 | null, 9 | Buffer.from( 10 | JSON.stringify({ 11 | graph: { 12 | nodes: { 13 | test: { data: { root: 'libs/test' } }, 14 | dep: { data: { root: 'libs/dep' } }, 15 | }, 16 | dependencies: { 17 | test: [ 18 | { 19 | type: 'static', 20 | target: 'dep', 21 | }, 22 | ], 23 | }, 24 | }, 25 | }), 26 | 'utf-8' 27 | ) 28 | ) 29 | ), 30 | })); 31 | 32 | describe('getProjectChangePaths', () => { 33 | it('can get paths', async () => { 34 | const paths = await getProjectChangePaths('', 'test'); 35 | 36 | expect(paths.length).toBe(2); 37 | expect(paths[0]).toBe('libs/test'); 38 | expect(paths[1]).toBe('libs/dep'); 39 | }); 40 | 41 | it('can get paths with working directory', async () => { 42 | const paths = await getProjectChangePaths('libs/test', 'test'); 43 | 44 | expect(paths.length).toBe(2); 45 | expect(paths[0]).toBe('libs/test'); 46 | expect(paths[1]).toBe('libs/dep'); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Pull Request Check 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | pull_request: 9 | branches: 10 | - main 11 | - beta 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: "20" 32 | cache: "npm" 33 | - run: npm ci 34 | - name: Lint 35 | run: npm run affected:lint -- --base=origin/${{ github.base_ref }} 36 | - name: Test 37 | run: npm run affected:test -- --base=origin/${{ github.base_ref }} 38 | - name: Build 39 | run: npm run affected:build -- --base=origin/${{ github.base_ref }} 40 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.component.css__tmpl__: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | margin: 0 auto; 4 | padding: 0; 5 | } 6 | 7 | @media (min-width: 640px) { 8 | .container { 9 | margin: 0 auto; 10 | padding: 3rem 1rem; 11 | width: 640px; 12 | } 13 | } 14 | @media (min-width: 768px) { 15 | .container { 16 | margin: 0 auto; 17 | padding: 3rem 1rem; 18 | width: 768px; 19 | } 20 | } 21 | @media (min-width: 1024px) { 22 | .container { 23 | padding: 3rem 1rem; 24 | margin: 0 auto; 25 | width: 1024px; 26 | } 27 | } 28 | @media (min-width: 1280px) { 29 | .container { 30 | padding: 3rem 1rem; 31 | margin: 0 auto; 32 | width: 1280px; 33 | } 34 | } 35 | 36 | .nextSteps { 37 | margin-top: 0; 38 | } 39 | 40 | .nextSteps li { 41 | margin-top: 0; 42 | margin-bottom: 0; 43 | line-height: 50px; 44 | } 45 | 46 | .nextSteps li button { 47 | margin: 20px 0 0 10px; 48 | } 49 | 50 | 51 | .footer { 52 | background-color: #f1f1f1; 53 | padding-top: 56px; 54 | padding-bottom: 56px; 55 | padding-left: 150px; 56 | padding-right: 150px; 57 | display: flex; 58 | flex-direction: row; 59 | flex-wrap: wrap; 60 | border-bottom: 16px solid #0081a2; 61 | } 62 | 63 | @media (max-width: 767px) { 64 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 65 | .body-content { 66 | padding-top: 50px; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Injectable, Component, OnInit } from '@angular/core'; 2 | import { AuthService } from '../services/auth.service'; 3 | import { User } from 'oidc-client'; 4 | 5 | @Component({ 6 | selector: '<%= projectName %>-app-auth-callback', 7 | templateUrl: './auth-callback.component.html', 8 | styleUrls: ['./auth-callback.component.css'], 9 | }) 10 | @Injectable({ 11 | providedIn: 'root', 12 | }) 13 | export class AuthCallbackComponent implements OnInit { 14 | public tokenType = ''; 15 | public accessToken = ''; 16 | 17 | constructor(private authService: AuthService) {} 18 | 19 | ngOnInit() { 20 | this.getAuth(); 21 | } 22 | 23 | getAuth() { 24 | if (!this.authService.isLoggedIn()) { 25 | this.authService 26 | .completeAuthentication() 27 | .catch((error) => { 28 | console.error(`could not complete authentication: ${error}`); 29 | }) 30 | .then(() => { 31 | this.setTokens(); 32 | }); 33 | } 34 | } 35 | 36 | setTokens() { 37 | if (this.authService.isLoggedIn()) { 38 | const user: User | null = this.authService.getUser(); 39 | if (user) { 40 | this.accessToken = user.access_token; 41 | this.tokenType = user.token_type; 42 | } 43 | return true; 44 | } else { 45 | return false; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Hello! This is where you manage which Jekyll version is used to run. 4 | # When you want to use a different version, change it below, save the 5 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 6 | # 7 | # bundle exec jekyll serve 8 | # 9 | # This will help ensure the proper Jekyll version is running. 10 | # Happy Jekylling! 11 | # gem "jekyll", "~> 3.9.0" 12 | 13 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 14 | # gem "minima", "~> 2.0" 15 | # gem "just-the-docs" 16 | 17 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 18 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 19 | gem "github-pages", "~> 228", group: :jekyll_plugins 20 | 21 | # If you have any plugins, put them here! 22 | group :jekyll_plugins do 23 | gem "jekyll-remote-theme", "~> 0.4.3" 24 | end 25 | 26 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 27 | # and associated library. 28 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do 29 | gem "tzinfo", "~> 1.2" 30 | gem "tzinfo-data" 31 | end 32 | 33 | # Performance-booster for watching directories on Windows 34 | gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform? 35 | 36 | # kramdown v2 ships without the gfm parser by default. If you're using 37 | # kramdown v1, comment out this line. 38 | gem "kramdown-parser-gfm" 39 | 40 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/mern/mern.spec.ts: -------------------------------------------------------------------------------- 1 | import { readProjectConfiguration } from '@nx/devkit'; 2 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 3 | 4 | import * as utils from '@abgov/nx-oc'; 5 | import { environments } from '@abgov/nx-oc'; 6 | import { Schema } from './schema'; 7 | import generator from './mern'; 8 | 9 | jest.mock('@abgov/nx-oc'); 10 | const utilsMock = utils as jest.Mocked; 11 | utilsMock.getAdspConfiguration.mockResolvedValue({ 12 | tenant: 'test', 13 | tenantRealm: 'test', 14 | accessServiceUrl: environments.test.accessServiceUrl, 15 | directoryServiceUrl: environments.test.directoryServiceUrl, 16 | }); 17 | 18 | describe.skip('React App Generator', () => { 19 | const options: Schema = { 20 | name: 'test', 21 | env: 'dev', 22 | }; 23 | 24 | it('can run', async () => { 25 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 26 | await generator(host, options); 27 | 28 | const appConfig = readProjectConfiguration(host, 'test-app'); 29 | expect(appConfig.root).toBe('apps/test-app'); 30 | 31 | const serviceConfig = readProjectConfiguration(host, 'test-service'); 32 | expect(serviceConfig.root).toBe('apps/test-service'); 33 | 34 | expect(host.exists('apps/test-app/nginx.conf')).toBeTruthy(); 35 | const nginxConf = host.read('apps/test-app/nginx.conf').toString(); 36 | expect(nginxConf).toContain('http://test-service:3333/'); 37 | }, 30000); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/app/app.spec.tsx__tmpl__: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { getDefaultMiddleware } from '@reduxjs/toolkit'; 4 | import { render } from '@testing-library/react'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | import configureStore from 'redux-mock-store'; 7 | 8 | import App from './app'; 9 | 10 | const mockStore = configureStore(getDefaultMiddleware()); 11 | 12 | describe('App', () => { 13 | let store, userManager; 14 | 15 | beforeEach(() => { 16 | store = mockStore({ 17 | user: {}, 18 | intake: {}, 19 | start: {} 20 | }); 21 | 22 | userManager = { 23 | signoutRedirect: jest.fn(), 24 | signinRedirect: jest.fn() 25 | } 26 | }); 27 | 28 | it('should render successfully', () => { 29 | const { baseElement } = render( 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | 37 | expect(baseElement).toBeTruthy(); 38 | }); 39 | 40 | it('should have a greeting as the title', () => { 41 | const { getByText } = render( 42 | 43 | 44 | 45 | 46 | 47 | ); 48 | 49 | expect(getByText('Welcome to <%= projectName %>!')).toBeTruthy(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "semantic-release-nuget", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/semantic-release-nuget/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["packages/semantic-release-nuget/**/*.ts"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nx/jest:jest", 16 | "outputs": ["{workspaceRoot}/coverage/packages/semantic-release-nuget"], 17 | "options": { 18 | "jestConfig": "packages/semantic-release-nuget/jest.config.ts", 19 | "passWithNoTests": true 20 | } 21 | }, 22 | "build": { 23 | "executor": "@nx/js:tsc", 24 | "outputs": ["{options.outputPath}"], 25 | "options": { 26 | "outputPath": "dist/packages/semantic-release-nuget", 27 | "tsConfig": "packages/semantic-release-nuget/tsconfig.lib.json", 28 | "packageJson": "packages/semantic-release-nuget/package.json", 29 | "main": "packages/semantic-release-nuget/src/index.ts", 30 | "assets": ["packages/semantic-release-nuget/*.md"] 31 | } 32 | }, 33 | "release": { 34 | "executor": "nx:run-commands", 35 | "options": { 36 | "command": "npx semantic-release -e ./packages/semantic-release-nuget/.releaserc.json" 37 | } 38 | } 39 | }, 40 | "tags": [] 41 | } 42 | -------------------------------------------------------------------------------- /packages/semantic-release-nuget/src/plugin/publish.ts: -------------------------------------------------------------------------------- 1 | import { PluginFunction } from '@semantic-release/semantic-release'; 2 | import { PublishContext } from 'semantic-release'; 3 | import { exec as execCb } from 'child_process'; 4 | import * as path from 'path'; 5 | import { promisify } from 'util'; 6 | import { NugetPluginConfig } from './config'; 7 | 8 | const exec = promisify(execCb); 9 | 10 | export const publish: PluginFunction = async ( 11 | config: NugetPluginConfig, 12 | context 13 | ) => { 14 | const { nupkgRoot, source, symbolSource, skipDuplicate, timeout } = config; 15 | const { 16 | cwd, 17 | env, 18 | // stdout, 19 | // stderr 20 | } = context; 21 | 22 | const basePath = nupkgRoot ? path.resolve(cwd, nupkgRoot) : cwd; 23 | 24 | const args = [ 25 | 'dotnet', 26 | 'nuget', 27 | 'push', 28 | '*.nupkg', 29 | source ? `--source ${source}` : null, 30 | env.NUGET_API_KEY ? `--api-key ${env.NUGET_API_KEY}` : null, 31 | symbolSource ? `--symbol-source ${symbolSource}` : null, 32 | env.NUGET_API_KEY ? `--symbol-api-key ${env.NUGET_API_KEY}` : null, 33 | skipDuplicate ? '--skip-duplicate' : null, 34 | timeout ? `--timeout ${timeout}` : null, 35 | ].filter((v) => !!v); 36 | 37 | const dotnetResult = await exec(args.join(' '), { env, cwd: basePath }); 38 | 39 | // dotnetResult.stdout.pipe(stdout, { end: false }); 40 | // dotnetResult.stderr.pipe(stderr, { end: false }); 41 | 42 | await dotnetResult; 43 | 44 | return false; 45 | }; 46 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/actions/openshift/environments.yml__tmpl__: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: rbac.authorization.k8s.io/v1 5 | kind: RoleBinding 6 | metadata: 7 | name: env_deploy_pullers 8 | namespace: <%= ocInfraProject %> 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: 'system:image-puller' 13 | subjects: 14 | <% ocEnvProjects.forEach(function(ocEnvProject){ -%> 15 | - apiGroup: rbac.authorization.k8s.io 16 | kind: Group 17 | name: 'system:serviceaccounts:<%= ocEnvProject %>' 18 | <% }); -%> 19 | - apiVersion: rbac.authorization.k8s.io/v1 20 | kind: RoleBinding 21 | metadata: 22 | name: pipeline_edit 23 | namespace: <%= ocInfraProject %> 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | kind: ClusterRole 27 | name: edit 28 | subjects: 29 | - kind: ServiceAccount 30 | name: github-actions 31 | namespace: <%= ocInfraProject %> 32 | <% ocEnvProjects.forEach(function(ocEnvProject){ -%> 33 | - apiVersion: rbac.authorization.k8s.io/v1 34 | kind: RoleBinding 35 | metadata: 36 | name: pipeline_edit 37 | namespace: <%= ocEnvProject %> 38 | roleRef: 39 | apiGroup: rbac.authorization.k8s.io 40 | kind: ClusterRole 41 | name: edit 42 | subjects: 43 | - kind: ServiceAccount 44 | name: github-actions 45 | namespace: <%= ocInfraProject %> 46 | <% }); -%> 47 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/nx-util.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import { readFile } from 'fs'; 3 | import { resolve } from 'path'; 4 | import { promisify } from 'util'; 5 | 6 | interface ProjectNode { 7 | data: { 8 | root: string 9 | } 10 | } 11 | 12 | interface Dependency { 13 | type: 'static' | 'implicit'; 14 | target: string; 15 | } 16 | 17 | interface DependencyGraph { 18 | nodes: Record; 19 | dependencies: Record; 20 | } 21 | 22 | const paths: Record = {} 23 | 24 | export const getProjectChangePaths = async (cwd: string, project: string) => { 25 | 26 | if (!paths[project]) { 27 | 28 | const file = resolve(cwd, `tmp/${project}-deps.json`); 29 | await promisify(exec)( 30 | [ 31 | 'npx', 32 | 'nx', 33 | 'dep-graph', 34 | '--focus', 35 | project, 36 | '--file', 37 | file 38 | ].join(' ') 39 | ); 40 | 41 | const result = await promisify(readFile)(file, 'utf8'); 42 | const { graph }: { graph: DependencyGraph } = JSON.parse(result); 43 | 44 | // Include the project's root as well as the roots of all its dependencies. 45 | paths[project] = [ 46 | graph.nodes[project].data.root, 47 | ...graph.dependencies[project] 48 | .map(d => 49 | d.type === 'static' ? 50 | graph.nodes[d.target].data.root : null 51 | ) 52 | .filter(d => !!d) 53 | ]; 54 | } 55 | 56 | return paths[project]; 57 | } 58 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxOcPipeline", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "pipeline": { 8 | "type": "string", 9 | "description": "Name of the OpenShift Pipeline.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What should be the name of the oc pipeline?" 15 | }, 16 | "infra": { 17 | "type": "string", 18 | "description": "Name of the OpenShift Project for build infrastructure.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "i", 24 | "x-prompt": "What project should be used for build infrastructure?" 25 | }, 26 | "type": { 27 | "type": "string", 28 | "description": "Jenkins or GitHub Actions based pipeline.", 29 | "alias": "t", 30 | "default": "actions" 31 | }, 32 | "envs": { 33 | "type": "string", 34 | "description": "Names of the OpenShift Projects for environments.", 35 | "alias": "e", 36 | "x-prompt": "What projects should be used for environments (dev test prod)?" 37 | }, 38 | "apply": { 39 | "type": "boolean", 40 | "description": "Flag indicating if the pipeline should be applied.", 41 | "alias": "a", 42 | "x-prompt": "Apply the pipeline to OpenShift?" 43 | } 44 | }, 45 | "required": [ 46 | "pipeline", 47 | "infra", 48 | "type", 49 | "envs" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/files/__fileName__/__fileName__.module.css__tmpl__: -------------------------------------------------------------------------------- 1 | .taskList { 2 | width: 80vw; 3 | height: 80vh; 4 | display: flex; 5 | flex-direction: column; 6 | 7 | .header { 8 | height: 6vh; 9 | flex-shrink: 0; 10 | flex-grow: 0; 11 | 12 | > div:first-child { 13 | display: flex; 14 | align-items: baseline; 15 | 16 | > span { 17 | margin-right: 14px; 18 | } 19 | 20 | > span:first-child { 21 | margin-left: 14px; 22 | } 23 | 24 | > :last-child { 25 | margin-left: auto; 26 | } 27 | } 28 | } 29 | 30 | .details { 31 | height: 0; 32 | transition: height 200ms; 33 | position: relative; 34 | flex-shrink: 0; 35 | 36 | > * { 37 | height: 100%; 38 | width: 100%; 39 | } 40 | 41 | .detailsPlaceholder { 42 | display: flex; 43 | flex-direction: column; 44 | 45 | > *:first-child { 46 | flex-grow: 1; 47 | } 48 | } 49 | } 50 | 51 | .details[data-opened='true'] { 52 | height: 74vh; 53 | } 54 | 55 | .list { 56 | flex-grow: 1; 57 | flex-shrink: 1; 58 | position: relative; 59 | overflow: hidden; 60 | 61 | > div:first-child { 62 | display: flex; 63 | flex-direction: row; 64 | 65 | > :first-child { 66 | margin-right: auto; 67 | } 68 | } 69 | } 70 | 71 | .loading { 72 | flex-grow: 2; 73 | flex-shrink: 0; 74 | height: 80vh; 75 | display: flex; 76 | > * { 77 | margin: auto; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/app/app.module.css__tmpl__: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | main { 6 | display: grid; 7 | grid-template-columns: repeat(6, auto); 8 | } 9 | 10 | section { 11 | grid-column: 2 / span 2; 12 | margin-top: 40px; 13 | margin-bottom: 40px; 14 | } 15 | 16 | .app { 17 | margin: 0; 18 | } 19 | 20 | .nextSteps { 21 | margin-top: 0; 22 | } 23 | 24 | .nextSteps li { 25 | margin-top: 0; 26 | margin-bottom: 0; 27 | line-height: 50px; 28 | } 29 | 30 | .nextSteps li button { 31 | margin: 20px 0 0 10px; 32 | } 33 | 34 | .github a:before { 35 | background: url("data:image/svg+xml,%3Csvg version='1.0' xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 255 255'%3E%3Cg transform='translate(0,255) scale(0.8,-0.8)'%0Afill='%23000000' stroke='none'%3E%3Cpath d='M72 300 c-48 -29 -72 -75 -72 -138 0 -60 17 -101 56 -131 35 -28 74%0A-28 74 -1 0 15 -7 20 -24 20 -14 0 -27 4 -30 9 -3 4 6 6 20 3 16 -3 30 1 35 9%0A14 22 11 26 -20 32 -35 7 -59 54 -42 85 6 12 11 30 11 42 0 14 4 19 12 14 18%0A-11 110 -13 131 -2 13 7 17 4 17 -12 0 -12 5 -30 11 -42 17 -31 -7 -78 -43%0A-85 -20 -4 -27 -10 -24 -20 4 -8 6 -28 6 -44 0 -24 4 -29 24 -29 29 0 72 33%0A91 70 8 16 15 53 15 82 0 63 -24 109 -72 138 -45 27 -131 27 -176 0z'/%3E%3C/g%3E%3C/svg%3E") 36 | no-repeat center center; 37 | } 38 | 39 | .footer { 40 | background-color: #f1f1f1; 41 | padding-top: 56px; 42 | padding-bottom: 56px; 43 | padding-left: 150px; 44 | padding-right: 150px; 45 | display: flex; 46 | flex-direction: row; 47 | flex-wrap: wrap; 48 | border-bottom: 16px solid #0081a2 49 | } 50 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-dotnet/react-dotnet.spec.ts: -------------------------------------------------------------------------------- 1 | import { readProjectConfiguration } from '@nx/devkit'; 2 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 3 | 4 | import * as utils from '@abgov/nx-oc'; 5 | import { environments } from '@abgov/nx-oc'; 6 | import serviceGenerator from '../dotnet-service/dotnet-service'; 7 | import { Schema } from './schema'; 8 | import generator from './react-dotnet'; 9 | 10 | jest.mock('@abgov/nx-oc'); 11 | const utilsMock = utils as jest.Mocked; 12 | utilsMock.getAdspConfiguration.mockResolvedValue({ 13 | tenant: 'test', 14 | tenantRealm: 'test', 15 | accessServiceUrl: environments.test.accessServiceUrl, 16 | directoryServiceUrl: environments.test.directoryServiceUrl, 17 | }); 18 | 19 | jest.mock('../dotnet-service/dotnet-service'); 20 | const serviceGeneratorMock = serviceGenerator as jest.Mocked< 21 | typeof serviceGenerator 22 | >; 23 | 24 | describe('React App Generator', () => { 25 | const options: Schema = { 26 | name: 'test', 27 | env: 'dev', 28 | }; 29 | 30 | it('can run', async () => { 31 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 32 | await generator(host, options); 33 | 34 | const appConfig = readProjectConfiguration(host, 'test-app'); 35 | expect(appConfig.root).toBe('apps/test-app'); 36 | 37 | expect(serviceGeneratorMock).toHaveBeenCalled(); 38 | 39 | expect(host.exists('apps/test-app/nginx.conf')).toBeTruthy(); 40 | const nginxConf = host.read('apps/test-app/nginx.conf').toString(); 41 | expect(nginxConf).toContain('http://test-service:5000/'); 42 | }, 30000); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/deployment/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxOcDeployment", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "project": { 8 | "type": "string", 9 | "description": "Project to add deployment in.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "Which project to add deployment for?" 15 | }, 16 | "appType": { 17 | "type": "string", 18 | "description": "Type of application.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "t", 24 | "x-prompt": { 25 | "message": "What type of application is the project?", 26 | "type": "list", 27 | "items": [ 28 | "frontend", 29 | "dotnet", 30 | "node" 31 | ] 32 | } 33 | }, 34 | "env": { 35 | "type": "string", 36 | "description": "Environment to target.", 37 | "$default": { 38 | "$source": "argv", 39 | "index": 2 40 | }, 41 | "alias": "e", 42 | "x-prompt": { 43 | "message": "Which ADSP environment do you want to target?", 44 | "type": "list", 45 | "items": [ 46 | "dev", 47 | "test", 48 | "prod" 49 | ] 50 | } 51 | }, 52 | "accessToken": { 53 | "type": "string", 54 | "description": "Access token for retrieving configuration from ADSP APIs.", 55 | "alias": "at" 56 | } 57 | }, 58 | "required": [ 59 | "project", 60 | "appType", 61 | "env" 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-dotnet/react-dotnet.ts: -------------------------------------------------------------------------------- 1 | import { getAdspConfiguration } from '@abgov/nx-oc'; 2 | import { 3 | getWorkspaceLayout, 4 | installPackagesTask, 5 | names, 6 | Tree, 7 | } from '@nx/devkit'; 8 | import initDotnetService from '../dotnet-service/dotnet-service'; 9 | import initReactApp from '../react-app/react-app'; 10 | import { NormalizedSchema, Schema } from './schema'; 11 | 12 | async function normalizeOptions( 13 | host: Tree, 14 | options: Schema 15 | ): Promise { 16 | const name = names(options.name).fileName; 17 | const projectDirectory = name; 18 | const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-'); 19 | const projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectDirectory}`; 20 | const openshiftDirectory = `.openshift/${projectDirectory}`; 21 | 22 | const adsp = await getAdspConfiguration(host, options); 23 | 24 | return { 25 | ...options, 26 | projectName, 27 | projectRoot, 28 | projectDirectory, 29 | openshiftDirectory, 30 | adsp, 31 | }; 32 | } 33 | 34 | export default async function (host: Tree, options: Schema) { 35 | const normalizedOptions = await normalizeOptions(host, options); 36 | 37 | await initDotnetService(host, { 38 | ...normalizedOptions, 39 | name: `${options.name}-service`, 40 | }); 41 | 42 | await initReactApp(host, { 43 | ...normalizedOptions, 44 | name: `${options.name}-app`, 45 | proxy: { 46 | location: '/api/', 47 | proxyPass: `http://${options.name}-service:5000/${options.name}-service/`, 48 | }, 49 | }); 50 | 51 | return () => { 52 | installPackagesTask(host); 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/lib.spec.ts: -------------------------------------------------------------------------------- 1 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 2 | import { 3 | Tree, 4 | readProjectConfiguration, 5 | addProjectConfiguration, 6 | writeJson, 7 | readJson, 8 | } from '@nx/devkit'; 9 | 10 | import generator from './lib'; 11 | import { Schema } from './schema'; 12 | 13 | describe('nx-release generator', () => { 14 | let appTree: Tree; 15 | const options: Schema = { project: 'test' }; 16 | 17 | beforeEach(() => { 18 | appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 19 | addProjectConfiguration(appTree, 'test', { 20 | root: 'libs/test', 21 | projectType: 'library', 22 | targets: { 23 | build: { 24 | executor: '@nx/node:package', 25 | options: { 26 | outputPath: 'dist/libs/test', 27 | }, 28 | }, 29 | }, 30 | }); 31 | }); 32 | 33 | it('should run successfully', async () => { 34 | await generator(appTree, options); 35 | const config = readProjectConfiguration(appTree, 'test'); 36 | 37 | expect(config.targets.release).toBeDefined(); 38 | expect(appTree.exists('.releaserc.json')).toBeTruthy(); 39 | expect(appTree.exists('libs/test/.releaserc.json')).toBeTruthy(); 40 | }); 41 | 42 | it('should skip root .releaserc.json if present', async () => { 43 | await generator(appTree, options); 44 | const config = readProjectConfiguration(appTree, 'test'); 45 | const releaseConfig = { value: 'a' }; 46 | writeJson(appTree, '.releaserc.json', releaseConfig); 47 | 48 | expect(config.targets.release).toBeDefined(); 49 | expect(readJson(appTree, '.releaserc.json')).toMatchObject(releaseConfig); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | title: Nx Tools Guide 17 | email: DIO@gov.ab.ca 18 | description: >- # this means to ignore newlines until "baseurl:" 19 | Government of Alberta plugins for nx guide. 20 | baseurl: "/nx-tools" # the subpath of your site, e.g. /blog 21 | url: "https://govalta.github.io" # the base hostname & protocol for your site, e.g. http://example.com 22 | # twitter_username: jekyllrb 23 | # github_username: jekyll 24 | 25 | # Build settings 26 | markdown: kramdown 27 | remote_theme: pmarsceill/just-the-docs 28 | plugins: 29 | - jekyll-remote-theme 30 | # - jekyll-feed 31 | 32 | # Exclude from processing. 33 | # The following items will not be processed, by default. Create a custom list 34 | # to override the default setting. 35 | # exclude: 36 | # - Gemfile 37 | # - Gemfile.lock 38 | # - node_modules 39 | # - vendor/bundle/ 40 | # - vendor/cache/ 41 | # - vendor/gems/ 42 | # - vendor/ruby/ 43 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.module.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { NgModule, APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import "@abgov/web-components"; 4 | import { AngularComponentsModule } from '@abgov/angular-components'; 5 | 6 | import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; 7 | import { AuthInterceptor } from './auth.interceptor'; 8 | import { FormsModule } from '@angular/forms'; 9 | import { AppRoutingModule } from './app.routes'; 10 | import { ProtectedComponent } from './protected/protected.component'; 11 | import { AuthCallbackComponent } from './auth-callback/auth-callback.component'; 12 | import { AuthGuardService } from './services/auth-guard.service'; 13 | import { AuthService } from './services/auth.service'; 14 | import { AppComponent } from './app.component'; 15 | import { Config } from '../environments/config'; 16 | 17 | @NgModule({ 18 | imports: [ 19 | AngularComponentsModule, 20 | BrowserModule, 21 | HttpClientModule, 22 | FormsModule, 23 | AppRoutingModule, 24 | ], 25 | providers: [ 26 | AuthGuardService, 27 | AuthService, 28 | Config, 29 | { 30 | provide: APP_INITIALIZER, 31 | useFactory: initializeApp, 32 | deps: [Config], 33 | multi: true, 34 | }, 35 | { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, 36 | ], 37 | declarations: [AppComponent, AuthCallbackComponent, ProtectedComponent], 38 | bootstrap: [AppComponent], 39 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 40 | }) 41 | export class AppModule {} 42 | 43 | export function initializeApp(config: Config) { 44 | return (): Promise => { 45 | return config.Init(); 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /packages/nx-oc/src/executors/apply/apply.spec.ts: -------------------------------------------------------------------------------- 1 | import { ExecutorContext } from '@nx/devkit'; 2 | import { mocked } from 'jest-mock'; 3 | import { Schema } from './schema'; 4 | import executor from './apply'; 5 | import { runOcCommand } from '../../utils/oc-utils'; 6 | 7 | jest.mock('../../utils/oc-utils'); 8 | const mockedRunOcCommand = mocked(runOcCommand); 9 | 10 | const options: Schema = { ocProject: 'test-dev' }; 11 | 12 | describe('Apply Executor', () => { 13 | beforeEach(() => { 14 | mockedRunOcCommand.mockReset(); 15 | }); 16 | 17 | it('can run', async () => { 18 | mockedRunOcCommand.mockReturnValue({ success: true }); 19 | 20 | const { success } = await executor(options, { 21 | projectName: 'test', 22 | } as ExecutorContext); 23 | expect(success).toBe(true); 24 | 25 | expect(mockedRunOcCommand.mock.calls.length).toBe(2); 26 | expect(mockedRunOcCommand.mock.calls[0][0]).toBe('process'); 27 | expect(mockedRunOcCommand.mock.calls[1][0]).toBe('apply'); 28 | }); 29 | 30 | it('can run for multiple environments', async () => { 31 | mockedRunOcCommand.mockReturnValue({ success: true }); 32 | 33 | const { success } = await executor( 34 | { ...options, ocProject: ['test-dev', 'test-qa'] }, 35 | { projectName: 'test' } as ExecutorContext 36 | ); 37 | 38 | expect(success).toBe(true); 39 | expect(mockedRunOcCommand.mock.calls.length).toBe(4); 40 | }); 41 | 42 | it('can return unsuccessful result', async () => { 43 | mockedRunOcCommand.mockReturnValue({ success: false }); 44 | 45 | const { success } = await executor(options, { 46 | projectName: 'test', 47 | } as ExecutorContext); 48 | 49 | expect(success).toBe(false); 50 | expect(mockedRunOcCommand.mock.calls.length).toBe(1); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/services/auth.service.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { UserManager, UserManagerSettings, User } from 'oidc-client'; 3 | 4 | @Injectable({ 5 | providedIn: 'root', 6 | }) 7 | export class AuthService { 8 | private manager = new UserManager(getClientSettings()); 9 | private user: User | null = null; 10 | 11 | constructor() { 12 | this.manager.getUser().then((user) => { 13 | this.user = user; 14 | }); 15 | } 16 | 17 | isLoggedIn(): boolean { 18 | return this.user != null && !this.user.expired; 19 | } 20 | 21 | getUser(): User | null { 22 | return this.user; 23 | } 24 | 25 | startAuthentication(): Promise { 26 | return this.manager.signinRedirect(); 27 | } 28 | 29 | completeAuthentication(): Promise { 30 | return this.manager.signinRedirectCallback().then((user) => { 31 | this.user = user; 32 | console.log(JSON.stringify(this.user)); 33 | }); 34 | } 35 | 36 | logout() { 37 | return this.manager.signoutRedirect(); 38 | } 39 | } 40 | 41 | export function getClientSettings(): UserManagerSettings { 42 | const data = JSON.parse(localStorage.getItem('envData') || "\"\""); 43 | const appUrl = `${window.location.protocol}//${window.location.hostname}${ 44 | window.location.port ? `:${window.location.port}` : '' 45 | }`; 46 | 47 | const settings = { 48 | client_id: data.access.client_id, 49 | redirect_uri: `${appUrl}/auth-callback`, 50 | post_logout_redirect_uri: `${appUrl}/signout/callback`, 51 | silent_redirect_uri: `${appUrl}/auth-callback`, 52 | response_type: 'code', 53 | authority: `${data.access.url}/auth/realms/${data.access.realm}`, 54 | automaticSilentRenew: true, 55 | }; 56 | return settings; 57 | } 58 | -------------------------------------------------------------------------------- /packages/nx-oc/src/executors/apply/apply.ts: -------------------------------------------------------------------------------- 1 | import { ExecutorContext } from '@nx/devkit'; 2 | import { pipelineEnvs as envs } from '../../pipeline-envs'; 3 | import { runOcCommand } from '../../utils/oc-utils'; 4 | import { NormalizedSchema, PipelineEnvironment, Schema } from './schema'; 5 | 6 | function mapOcProject( 7 | project: string | PipelineEnvironment, 8 | i: number 9 | ): PipelineEnvironment { 10 | if (typeof project === 'string') { 11 | return { project: 'string', tag: envs[i].toLowerCase() }; 12 | } else { 13 | return project; 14 | } 15 | } 16 | 17 | function normalizeSchema(options: Schema): NormalizedSchema { 18 | const ocProjects = Array.isArray(options.ocProject) 19 | ? options.ocProject.map(mapOcProject) 20 | : [mapOcProject(options.ocProject, 0)]; 21 | 22 | return { ocProjects }; 23 | } 24 | 25 | export default async function runExecutor( 26 | options: Schema, 27 | { projectName }: ExecutorContext 28 | ): Promise<{ success: boolean }> { 29 | console.log(`Running oc apply for ${projectName}...`); 30 | 31 | const { ocProjects } = normalizeSchema(options); 32 | 33 | const failed = ocProjects 34 | .map(({ project, tag }) => { 35 | const processResult = runOcCommand('process', [ 36 | `-f .openshift/${projectName}/${projectName}.yml`, 37 | `-p PROJECT=${project}`, 38 | `-p DEPLOY_TAG=${tag}`, 39 | ]); 40 | 41 | if (!processResult.success) { 42 | return false; 43 | } else { 44 | const { success, stdout } = runOcCommand( 45 | 'apply', 46 | [], 47 | processResult.stdout 48 | ); 49 | console.log(stdout?.toString()); 50 | 51 | return success; 52 | } 53 | }) 54 | .filter((success) => !success); 55 | 56 | return { 57 | success: !failed.length, 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /.github/workflows/release-ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Release CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: 10 | - main 11 | - beta 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: "20" 32 | cache: "npm" 33 | - run: npm ci 34 | - name: Get last successful commit 35 | id: last_successful_commit 36 | uses: nrwl/nx-set-shas@v3 37 | - name: Lint 38 | run: npm run affected:lint -- --base=${{ steps.last_successful_commit.outputs.base }} 39 | - name: Test 40 | run: npm run affected:test -- --base=${{ steps.last_successful_commit.outputs.base }} 41 | - name: Build 42 | run: npm run affected:build -- --base=${{ steps.last_successful_commit.outputs.base }} 43 | - name: Release 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 47 | run: npx nx affected --target release --base=${{ steps.last_successful_commit.outputs.base }} 48 | -------------------------------------------------------------------------------- /packages/nx-adsp/README.md: -------------------------------------------------------------------------------- 1 | # About this project 2 | 3 | This is the Government of Alberta - Nx plugin for ADSP apps. 4 | 5 | The plugin includes generators for application, service, and fullstack solution quick starts. If the peer dependency `@abgov/nx-oc` is found, then OpenShift yml files are also included in the quickstarts. Application stack peer dependencies are required for the associated ADSP application type; for example, `@nx/angular` is required for the angular-app, and [@nx-dotnet/core](https://github.com/nx-dotnet/nx-dotnet) is required for dotnet-service. 6 | 7 | ## TLDR 8 | 9 | 1. Create your Nx workspace using `npx create-nx-workspace my-workspace` 10 | 2. Install plugin: `npm i -D @abgov/nx-adsp` 11 | 3. Generate a quick start `npx g @abgov/nx-adsp:express-service my-app` 12 | 13 | # Dev Features 14 | 15 | Stuff inside [] needs to be replaced with your own names 16 | 17 | ## To add a plugin to @abgov/nx-adsp 18 | 19 | nx g @nx/plugin:plugin [pluginName] --importPath . 20 | move the code generated into packages/ns-adsp and make changes in the plugin directory you've just generated 21 | 22 | ## To test the plugin locally 23 | 24 | 1. Generate a temp folder with a local nx-adsp using `nx run nx-adsp-e2e:e2e` 25 | 2. Generate a new project somewhere with `npx create-nx-workspace`, following the prompts 26 | 27 | 3. Inside the new workspace, 28 | i) run 29 | npm i -D [location-of-nx-tools]/dist/packages/nx-adsp //to update package.json 30 | npm i -D @nx/angular //(this assumes you are building an angular plugin) 31 | 32 | ii) generate the plugin - tenantRealm is the realm UUID used to generate a login feature that will log into that particular tenant (eg '2a9a2c30-a094-4097-9247-8d41b39cb80e') 33 | `npx nx g @abgov/nx-adsp:[pluginName] my-ang-app --tenant tenantRealm` 34 | iii) run `npm install` 35 | iv) serve the plugin using `nx serve [pluginName] 36 | -------------------------------------------------------------------------------- /packages/nx-oc/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-oc", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/nx-oc/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "options": { 10 | "lintFilePatterns": [ 11 | "packages/nx-oc/**/*.ts", 12 | "packages/nx-oc/package.json" 13 | ] 14 | }, 15 | "outputs": ["{options.outputFile}"] 16 | }, 17 | "test": { 18 | "executor": "@nx/jest:jest", 19 | "outputs": ["{workspaceRoot}/coverage/packages/nx-oc"], 20 | "options": { 21 | "jestConfig": "packages/nx-oc/jest.config.ts", 22 | "passWithNoTests": true 23 | } 24 | }, 25 | "build": { 26 | "executor": "@nx/js:tsc", 27 | "outputs": ["{options.outputPath}"], 28 | "options": { 29 | "outputPath": "dist/packages/nx-oc", 30 | "tsConfig": "packages/nx-oc/tsconfig.lib.json", 31 | "packageJson": "packages/nx-oc/package.json", 32 | "main": "packages/nx-oc/src/index.ts", 33 | "assets": [ 34 | "packages/nx-oc/*.md", 35 | { 36 | "input": "./packages/nx-oc/src", 37 | "glob": "**/*.!(ts)", 38 | "output": "./src" 39 | }, 40 | { 41 | "input": "./packages/nx-oc", 42 | "glob": "generators.json", 43 | "output": "." 44 | }, 45 | { 46 | "input": "./packages/nx-oc", 47 | "glob": "executors.json", 48 | "output": "." 49 | } 50 | ] 51 | } 52 | }, 53 | "release": { 54 | "executor": "nx:run-commands", 55 | "options": { 56 | "command": "npx semantic-release -e ./packages/nx-oc/.releaserc.json" 57 | } 58 | } 59 | }, 60 | "tags": [] 61 | } 62 | -------------------------------------------------------------------------------- /packages/nx-adsp/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-adsp", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/nx-adsp/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "options": { 10 | "lintFilePatterns": [ 11 | "packages/nx-adsp/**/*.ts", 12 | "packages/nx-adsp/package.json" 13 | ] 14 | }, 15 | "outputs": ["{options.outputFile}"] 16 | }, 17 | "test": { 18 | "executor": "@nx/jest:jest", 19 | "outputs": ["{workspaceRoot}/coverage/packages/nx-adsp"], 20 | "options": { 21 | "jestConfig": "packages/nx-adsp/jest.config.ts", 22 | "passWithNoTests": true 23 | } 24 | }, 25 | "build": { 26 | "executor": "@nx/js:tsc", 27 | "outputs": ["{options.outputPath}"], 28 | "options": { 29 | "outputPath": "dist/packages/nx-adsp", 30 | "tsConfig": "packages/nx-adsp/tsconfig.lib.json", 31 | "packageJson": "packages/nx-adsp/package.json", 32 | "main": "packages/nx-adsp/src/index.ts", 33 | "assets": [ 34 | "packages/nx-adsp/*.md", 35 | { 36 | "input": "./packages/nx-adsp/src", 37 | "glob": "**/*.!(ts)", 38 | "output": "./src" 39 | }, 40 | { 41 | "input": "./packages/nx-adsp", 42 | "glob": "generators.json", 43 | "output": "." 44 | }, 45 | { 46 | "input": "./packages/nx-adsp", 47 | "glob": "executors.json", 48 | "output": "." 49 | } 50 | ] 51 | } 52 | }, 53 | "release": { 54 | "executor": "nx:run-commands", 55 | "options": { 56 | "command": "npx semantic-release -e ./packages/nx-adsp/.releaserc.json" 57 | } 58 | } 59 | }, 60 | "tags": [] 61 | } 62 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/wrap-plugin.ts: -------------------------------------------------------------------------------- 1 | import { workspaceRoot } from '@nx/devkit'; 2 | import type { 3 | PluginConfig, 4 | PluginFunction, 5 | } from '@semantic-release/semantic-release'; 6 | import type { BaseContext } from 'semantic-release'; 7 | import { getPathCommitHashes } from './git-utils.js'; 8 | import { getProjectChangePaths } from './nx-util.js'; 9 | 10 | export interface WrappedPluginConfig extends PluginConfig { 11 | project: string; 12 | } 13 | 14 | export const wrapPlugin = 15 | (plugin: PluginFunction) => 16 | async ({ project, ...pluginConfig }: WrappedPluginConfig, context: T) => { 17 | const { logger } = context; 18 | const commits = context['commits']; 19 | let filteredCommits = commits; 20 | 21 | if (!project) { 22 | logger.log( 23 | "Skipping filtering of commits; no 'project' in configuration..." 24 | ); 25 | } else { 26 | logger.log(`Filtering commits to those affecting ${project}...`); 27 | 28 | const paths = await getProjectChangePaths(workspaceRoot, project); 29 | logger.log(`Resolved to paths: ${paths.join(', ')}...`); 30 | 31 | const from = context['lastRelease']?.gitHead; 32 | const to = context['nextRelease']?.gitHead; 33 | 34 | const hashes = await getPathCommitHashes( 35 | workspaceRoot, 36 | project, 37 | paths, 38 | from, 39 | to 40 | ); 41 | 42 | filteredCommits = commits.filter((commit) => { 43 | const match = hashes.includes(commit.commit.long); 44 | if (match) { 45 | logger.log( 46 | `Including commit ${commit.commit.short}: ${commit.message}` 47 | ); 48 | } 49 | return match; 50 | }); 51 | } 52 | 53 | return await plugin(pluginConfig, { 54 | ...context, 55 | commits: [...filteredCommits] as const, 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.component.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { TestBed, getTestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | import { AngularComponentsModule } from '@abgov/angular-components'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from "@angular/platform-browser-dynamic/testing"; 9 | 10 | 11 | describe('AppComponent', () => { 12 | beforeAll( ()=> { 13 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 14 | }); 15 | beforeEach(async () => { 16 | let envData = {"production":false,"access":{"url":"https://testurl.com","realm":"123","client_id":"urn:ads:platform:tenant-admin-app"},"tenantApi":{"host":"http://localhost:3333","endpoints":{"tenantNameByRealm":"/api/tenant/v1/realm"}}} 17 | 18 | localStorage.setItem('envData', JSON.stringify(envData)); 19 | await TestBed.configureTestingModule({ 20 | declarations: [AppComponent], 21 | imports: [AngularComponentsModule, RouterTestingModule], 22 | }).compileComponents(); 23 | }); 24 | 25 | it('should create the app', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | const app = fixture.componentInstance; 28 | expect(app).toBeTruthy(); 29 | }); 30 | 31 | it(`should have as title '<%= projectName %>'`, () => { 32 | const fixture = TestBed.createComponent(AppComponent); 33 | const app = fixture.componentInstance; 34 | expect(app.title).toEqual('<%= projectName %>'); 35 | }); 36 | 37 | it('should render title', () => { 38 | const fixture = TestBed.createComponent(AppComponent); 39 | fixture.detectChanges(); 40 | const compiled = fixture.nativeElement; 41 | expect(compiled.querySelector('h2').textContent).toContain( 42 | 'Welcome to <%= projectName %>!' 43 | ); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/nx-release/README.md: -------------------------------------------------------------------------------- 1 | # About this project 2 | This is the Government of Alberta - Nx plugin for Semantic Release. 3 | 4 | The project contains both the Nx plugin generator for setting up a monorepo with semantic release 5 | configuration as well as a plugin for semantic release. 6 | 7 | ## Semantic release / monorepo 8 | There are multiple challenges with using semantic release in a monorepo: 9 | 1. Each release project needs distinct release tags. 10 | 2. Each project should be evaluated based on only commits related to it. 11 | 3. Interdependencies between projects need to be handled. 12 | 4. Interdependencies between publishable/releasable projects require coordination of releases. 13 | 5. Channel (next-major, next, latest) information is stored in a git note on a semantic-release ref and does not distinguished between tags (and consequently projects). 14 | 15 | For (1), (2), and (3) in part this project uses the approach from [semantic-release-monorepo](https://github.com/pmowrer/semantic-release-monorepo) along with Nx capabilities: 16 | - Each project uses a distinct `tagFormat`; 17 | - A custom plugin wraps default plugins for `analyzeCommits` and `generateNotes` and filters for only relevant commits; 18 | - Nx `dep-graph` is used to determine dependency paths that should also be included. 19 | 20 | (4) is not currently handled, but a solution leveraging Nx capabilities is the general direction. For example, Nx supports ordered execution of tasks based on 'affected'. 21 | 22 | 23 | 24 | **KNOWN ISSUE** (5) from above is not handled, and non-prerelease channel upgrades will result in only the first project being upgraded when release tags from multiple projects are on the same commit. 25 | 26 | In Semantic Release the channel upgrade is done separately from the next version analysis and there are no extension hooks to modify the behaviour. Prerelease channels with a distinct version format (e.g. 1.0.0-beta.x) are excluded from that process and appear to work. 27 | -------------------------------------------------------------------------------- /e2e/nx-adsp-e2e/tests/nx-adsp.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | checkFilesExist, 3 | ensureNxProject, 4 | runNxCommandAsync, 5 | uniq, 6 | } from '@nx/plugin/testing'; 7 | 8 | describe('nx-adsp e2e', () => { 9 | beforeEach(() => ensureNxProject('@abgov/nx-adsp', 'dist/packages/nx-adsp')); 10 | 11 | describe('express service', () => { 12 | it('should create express-service', async (done) => { 13 | const plugin = uniq('express-service'); 14 | const result = await runNxCommandAsync( 15 | `generate @abgov/nx-adsp:express-service ${plugin} test` 16 | ); 17 | expect(() => checkFilesExist(`apps/${plugin}/src/main.ts`)).not.toThrow(); 18 | 19 | done(); 20 | }, 60000); 21 | 22 | describe('--tenant', () => { 23 | it('should create express-service', async (done) => { 24 | const plugin = uniq('express-service'); 25 | const result = await runNxCommandAsync( 26 | `generate @abgov/nx-adsp:express-service ${plugin} --tenant test` 27 | ); 28 | expect(() => 29 | checkFilesExist(`apps/${plugin}/src/main.ts`) 30 | ).not.toThrow(); 31 | 32 | done(); 33 | }, 60000); 34 | }); 35 | }); 36 | 37 | describe('react app', () => { 38 | it('should create react-app', async (done) => { 39 | const plugin = uniq('react-app'); 40 | const result = await runNxCommandAsync( 41 | `generate @abgov/nx-adsp:react-app ${plugin} test` 42 | ); 43 | expect( 44 | () => {} // checkFilesExist(`apps/${plugin}/src/main.ts`) 45 | ).not.toThrow(); 46 | 47 | done(); 48 | }, 60000); 49 | }); 50 | 51 | it('should create angular app', async (done) => { 52 | const plugin = uniq('angular-app'); 53 | await runNxCommandAsync( 54 | `generate @abgov/nx-adsp:angular-app ${plugin} test` 55 | ); 56 | 57 | const result = await runNxCommandAsync(`build ${plugin}`); 58 | expect(result.stdout).toContain('Executor ran'); 59 | 60 | done(); 61 | }, 60000); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/dotnet-service.spec.ts: -------------------------------------------------------------------------------- 1 | import { addDependenciesToPackageJson } from '@nx/devkit'; 2 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 3 | import appGenerator from '@nx-dotnet/core/src/generators/app/generator'; 4 | import refGenerator from '@nx-dotnet/core/src/generators/nuget-reference/generator'; 5 | 6 | import * as utils from '@abgov/nx-oc'; 7 | import { environments } from '@abgov/nx-oc'; 8 | import { Schema } from './schema'; 9 | import generator from './dotnet-service'; 10 | 11 | jest.mock('@nx-dotnet/core/src/generators/app/generator'); 12 | jest.mock('@nx-dotnet/core/src/generators/nuget-reference/generator'); 13 | 14 | const appGeneratorMock = appGenerator as jest.Mocked; 15 | const refGeneratorMock = refGenerator as jest.Mocked; 16 | 17 | jest.mock('@abgov/nx-oc', () => ({ 18 | ...jest.requireActual('@abgov/nx-oc'), 19 | getAdspConfiguration: jest.fn(), 20 | deploymentGenerator: jest.fn(), 21 | })); 22 | const utilsMock = utils as jest.Mocked; 23 | utilsMock.getAdspConfiguration.mockResolvedValue({ 24 | tenant: 'test', 25 | tenantRealm: 'test', 26 | accessServiceUrl: environments.test.accessServiceUrl, 27 | directoryServiceUrl: environments.test.directoryServiceUrl, 28 | }); 29 | utilsMock.deploymentGenerator.mockResolvedValue(); 30 | 31 | describe('Dotnet Service Generator', () => { 32 | const options: Schema = { 33 | name: 'test', 34 | env: 'dev', 35 | }; 36 | 37 | it('can run', async () => { 38 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 39 | addDependenciesToPackageJson( 40 | host, 41 | {}, 42 | { 43 | '@nx-dotnet/core': '^1.16.0', 44 | } 45 | ); 46 | await generator(host, options); 47 | expect(appGeneratorMock).toHaveBeenCalled(); 48 | expect(refGeneratorMock).toHaveBeenCalled(); 49 | expect(utils.deploymentGenerator).toHaveBeenCalled(); 50 | host.exists('apps/test/Program.cs'); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "AngularApp", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the application.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | }, 39 | "proxy": { 40 | "oneOf": [ 41 | { 42 | "type": "array", 43 | "items": { 44 | "type": "object", 45 | "properties": { 46 | "location": { 47 | "type": "string" 48 | }, 49 | "proxyPass": { 50 | "type": "string" 51 | } 52 | }, 53 | "required": [ 54 | "location", 55 | "proxyPass" 56 | ] 57 | } 58 | }, 59 | { 60 | "type": "object", 61 | "properties": { 62 | "location": { 63 | "type": "string" 64 | }, 65 | "proxyPass": { 66 | "type": "string" 67 | } 68 | }, 69 | "required": [ 70 | "location", 71 | "proxyPass" 72 | ] 73 | } 74 | ] 75 | } 76 | }, 77 | "required": [ 78 | "name", 79 | "env" 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "NxAdspReactApp", 4 | "title": "", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "Name of the application.", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use?" 15 | }, 16 | "env": { 17 | "type": "string", 18 | "description": "Environment to target.", 19 | "$default": { 20 | "$source": "argv", 21 | "index": 1 22 | }, 23 | "alias": "e", 24 | "x-prompt": { 25 | "message": "Which ADSP environment do you want to target?", 26 | "type": "list", 27 | "items": [ 28 | "dev", 29 | "test", 30 | "prod" 31 | ] 32 | } 33 | }, 34 | "accessToken": { 35 | "type": "string", 36 | "description": "Access token for retrieving configuration from ADSP APIs.", 37 | "alias": "at" 38 | }, 39 | "proxy": { 40 | "oneOf": [ 41 | { 42 | "type": "array", 43 | "items": { 44 | "type": "object", 45 | "properties": { 46 | "location": { 47 | "type": "string" 48 | }, 49 | "proxyPass": { 50 | "type": "string" 51 | } 52 | }, 53 | "required": [ 54 | "location", 55 | "proxyPass" 56 | ] 57 | } 58 | }, 59 | { 60 | "type": "object", 61 | "properties": { 62 | "location": { 63 | "type": "string" 64 | }, 65 | "proxyPass": { 66 | "type": "string" 67 | } 68 | }, 69 | "required": [ 70 | "location", 71 | "proxyPass" 72 | ] 73 | } 74 | ] 75 | } 76 | }, 77 | "required": [ 78 | "name", 79 | "env" 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /packages/nx-release/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-release", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/nx-release/src", 5 | "projectType": "library", 6 | "targets": { 7 | "lint": { 8 | "executor": "@nx/eslint:lint", 9 | "options": { 10 | "lintFilePatterns": [ 11 | "packages/nx-release/**/*.ts", 12 | "packages/nx-release/package.json" 13 | ] 14 | }, 15 | "outputs": ["{options.outputFile}"] 16 | }, 17 | "test": { 18 | "executor": "@nx/jest:jest", 19 | "outputs": ["{workspaceRoot}/coverage/packages/nx-release"], 20 | "options": { 21 | "jestConfig": "packages/nx-release/jest.config.ts", 22 | "passWithNoTests": true 23 | } 24 | }, 25 | "build": { 26 | "executor": "@nx/js:tsc", 27 | "outputs": ["{options.outputPath}"], 28 | "options": { 29 | "outputPath": "dist/packages/nx-release", 30 | "tsConfig": "packages/nx-release/tsconfig.lib.json", 31 | "packageJson": "packages/nx-release/package.json", 32 | "main": "packages/nx-release/src/index.ts", 33 | "assets": [ 34 | "packages/nx-release/*.md", 35 | { 36 | "input": "./packages/nx-release/src", 37 | "glob": "**/*.!(ts)", 38 | "output": "./src" 39 | }, 40 | { 41 | "input": "./packages/nx-release/src", 42 | "glob": "**/.*", 43 | "output": "./src" 44 | }, 45 | { 46 | "input": "./packages/nx-release", 47 | "glob": "generators.json", 48 | "output": "." 49 | }, 50 | { 51 | "input": "./packages/nx-release", 52 | "glob": "executors.json", 53 | "output": "." 54 | } 55 | ] 56 | } 57 | }, 58 | "release": { 59 | "executor": "nx:run-commands", 60 | "options": { 61 | "command": "npx semantic-release -e ./packages/nx-release/.releaserc.json" 62 | } 63 | } 64 | }, 65 | "tags": [] 66 | } 67 | -------------------------------------------------------------------------------- /packages/nx-release/src/generators/lib/lib.ts: -------------------------------------------------------------------------------- 1 | import { 2 | addDependenciesToPackageJson, 3 | formatFiles, 4 | generateFiles, 5 | getWorkspaceLayout, 6 | installPackagesTask, 7 | offsetFromRoot, 8 | readProjectConfiguration, 9 | Tree, 10 | updateProjectConfiguration, 11 | } from '@nx/devkit'; 12 | import * as path from 'path'; 13 | import { NormalizedSchema, Schema } from './schema'; 14 | 15 | function addFiles(host: Tree, options: NormalizedSchema) { 16 | const templateOptions = { 17 | ...options, 18 | offsetFromRoot: offsetFromRoot(options.projectRoot), 19 | tmpl: '', 20 | }; 21 | 22 | if (!host.exists('.releaserc.json')) { 23 | generateFiles( 24 | host, 25 | path.join(__dirname, 'root-files'), 26 | '.', 27 | templateOptions 28 | ); 29 | } 30 | 31 | generateFiles( 32 | host, 33 | path.join(__dirname, 'files'), 34 | options.projectRoot, 35 | templateOptions 36 | ); 37 | } 38 | 39 | export default async function (host: Tree, options: Schema) { 40 | const config = readProjectConfiguration(host, options.project); 41 | const { build } = config.targets; 42 | if (config.projectType !== 'library' || !build) { 43 | console.log('This generator can only be run against buildable libraries.'); 44 | } else { 45 | addDependenciesToPackageJson( 46 | host, 47 | {}, 48 | { 49 | 'semantic-release': '^23.0.0', 50 | } 51 | ); 52 | 53 | config.targets.release = { 54 | executor: 'nx:run-commands', 55 | options: { 56 | command: `npx semantic-release -e ./${config.root}/.releaserc.json`, 57 | }, 58 | }; 59 | 60 | updateProjectConfiguration(host, options.project, config); 61 | 62 | const { libsDir } = getWorkspaceLayout(host); 63 | const normalizedOptions = { 64 | ...options, 65 | projectRoot: config.root, 66 | projectDist: 67 | build.options.outputPath || `dist/${libsDir}/${options.project}`, 68 | }; 69 | 70 | addFiles(host, normalizedOptions); 71 | await formatFiles(host); 72 | 73 | return () => { 74 | installPackagesTask(host); 75 | }; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/app.component.html__tmpl__: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 8 | 9 |
10 |
11 |

Welcome to {{ title }}!

12 |

13 | Don't panic. Start editing the project to build your digital service. 14 |

15 |

A few things you might want to do next:

16 |
    17 |
  • Create the 'my-app' client in your realm to let users sign in
  • 18 |
  • 19 | Make requests to the backend API by either updating nginx.conf or 20 | enabling CORS on the API. 21 |
  • 22 |
  • Add requests to public API resources:
  • 23 |
  • Add requests to private API resources:
  • 24 |
25 |
26 |
27 |
28 |

Home | Admin Area

29 |
30 | 31 | Sign Out 32 | 33 |
34 | 35 | 36 | Sign In 37 | 38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 | 58 |
59 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/angular-app/files/src/app/protected/protected.component.spec.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync, inject } from '@angular/core/testing'; 2 | import { ProtectedComponent } from './protected.component'; 3 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; 4 | import TenantService from '../tenant.service'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from "@angular/platform-browser-dynamic/testing"; 9 | 10 | describe('ProtectedComponent', () => { 11 | let fixture: ComponentFixture; 12 | let httpTestingController: HttpTestingController; 13 | let envData = {"production":false,"access":{"url":"https://testurl.com","realm":"123","client_id":"urn:ads:platform:tenant-admin-app"},"tenantApi":{"host":"http://localhost:3333","endpoints":{"tenantNameByRealm":"/api/tenant/v1/realm"}}} 14 | 15 | localStorage.setItem('envData', JSON.stringify(envData)); 16 | beforeAll( ()=> { 17 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 18 | }); 19 | beforeEach( 20 | waitForAsync(() => { 21 | TestBed.configureTestingModule({ 22 | declarations: [ProtectedComponent], 23 | providers: [ProtectedComponent, TenantService], 24 | imports: [HttpClientTestingModule], 25 | }).compileComponents(); 26 | 27 | httpTestingController = TestBed.inject(HttpTestingController); 28 | }) 29 | ); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(ProtectedComponent); 33 | fixture.detectChanges(); 34 | }); 35 | 36 | afterEach(() => { 37 | httpTestingController.verify(); 38 | }); 39 | 40 | it('should create', (inject([TenantService, HttpTestingController], 41 | () => { 42 | const mockTenant = { status: 200, statusText: 'OK'} 43 | const data = { name: 'Child Services' } 44 | const req = httpTestingController.expectOne( 45 | 'http://localhost:3333/api/tenant/v1/realm/123' 46 | ) 47 | 48 | expect(req.request.method).toEqual('GET'); 49 | 50 | req.flush(data, mockTenant); 51 | }))); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/files/src/main.ts__tmpl__: -------------------------------------------------------------------------------- 1 | /** 2 | * This is not a production server yet! 3 | * This is only a minimal backend to get started. 4 | */ 5 | import { AdspId, initializeService } from '@abgov/adsp-service-sdk'; 6 | import express from 'express'; 7 | import passport from 'passport'; 8 | import { Strategy as AnonymousStrategy } from 'passport-anonymous'; 9 | 10 | import { environment } from './environments/environment'; 11 | 12 | async function initializeApp(): Promise { 13 | const app = express(); 14 | app.use(passport.initialize()); 15 | 16 | const serviceId = AdspId.parse(environment.CLIENT_ID); 17 | const { tenantStrategy } = await initializeService( 18 | { 19 | displayName: '<%= projectName %>', 20 | description: 'Put your service description here.', 21 | realm: environment.TENANT_REALM, 22 | serviceId, 23 | clientSecret: environment.CLIENT_SECRET, 24 | accessServiceUrl: new URL(environment.ACCESS_SERVICE_URL), 25 | directoryUrl: new URL(environment.DIRECTORY_SERVICE_URL), 26 | }, 27 | { logLevel: environment.LOG_LEVEL } 28 | ); 29 | 30 | passport.use('tenant', tenantStrategy); 31 | passport.use('anonymous', new AnonymousStrategy()); 32 | 33 | app.use(passport.authenticate(['tenant', 'anonymous'], { session: false })); 34 | 35 | app.get('/<%= projectName %>/v1/public', (_req, res) => { 36 | res.send({ message: `Welcome to public API resource!` }); 37 | }); 38 | 39 | app.get( 40 | '/<%= projectName %>/v1/private', 41 | (req, res, next) => { 42 | if (!req.user) { 43 | res.sendStatus(401); 44 | } else { 45 | next(); 46 | } 47 | }, 48 | (req, res) => { 49 | const user = req.user; 50 | res.send({ message: `Welcome to private API resource! ${user.name}` }); 51 | } 52 | ); 53 | return app; 54 | } 55 | 56 | initializeApp().then((app) => { 57 | const port = environment.port || 3333; 58 | 59 | const server = app.listen(port, () => { 60 | console.log(`Listening at http://localhost:${port}/<%= projectName %>/v1`); 61 | }); 62 | server.on('error', console.error); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/nx-adsp/generators.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "name": "nx-adsp", 4 | "version": "0.0.1", 5 | "generators": { 6 | "express-service": { 7 | "factory": "./src/generators/express-service/express-service", 8 | "schema": "./src/generators/express-service/schema.json", 9 | "description": "Generator that creates an Node/Express based backend service." 10 | }, 11 | "react-app": { 12 | "factory": "./src/generators/react-app/react-app", 13 | "schema": "./src/generators/react-app/schema.json", 14 | "description": "Generator that creates a React/Redux based frontend application." 15 | }, 16 | "mern": { 17 | "factory": "./src/generators/mern/mern", 18 | "schema": "./src/generators/mern/schema.json", 19 | "description": "Generator that creates a MERN fullstack solution.", 20 | "hidden": true 21 | }, 22 | "dotnet-service": { 23 | "factory": "./src/generators/dotnet-service/dotnet-service", 24 | "schema": "./src/generators/dotnet-service/schema.json", 25 | "description": "Generator that creates a ASP.NET Core backend service." 26 | }, 27 | "react-dotnet": { 28 | "factory": "./src/generators/react-dotnet/react-dotnet", 29 | "schema": "./src/generators/react-dotnet/schema.json", 30 | "description": "Generator that creates a React-Dotnet fullstack solution." 31 | }, 32 | "react-form": { 33 | "factory": "./src/generators/react-form/react-form", 34 | "schema": "./src/generators/react-form/schema.json", 35 | "description": "Generator that creates a React component based on an ADSP Form Definition." 36 | }, 37 | "react-task-list": { 38 | "factory": "./src/generators/react-task-list/react-task-list", 39 | "schema": "./src/generators/react-task-list/schema.json", 40 | "description": "Generator that creates a React component based on an ADSP Task Queue." 41 | }, 42 | "angular-app": { 43 | "factory": "./src/generators/angular-app/angular-app", 44 | "schema": "./src/generators/angular-app/schema.json", 45 | "description": "Generator that creates a Angular based frontend application." 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/app/config.slice.ts__tmpl__: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; 2 | 3 | export const CONFIG_FEATURE_KEY = 'config'; 4 | 5 | export interface ConfigState { 6 | initialized: boolean; 7 | environment: Record; 8 | directory: Record; 9 | } 10 | 11 | export const initializeConfig = createAsyncThunk( 12 | 'config/initialize', 13 | async (environment?: Record) => { 14 | // Initialize state with environment and ADSP directory of services. 15 | const directoryUrl = environment?.directory?.['url']; 16 | 17 | let directory: Record = {}; 18 | if (directoryUrl) { 19 | const platform: { urn: string; url: string }[] = await ( 20 | await fetch(`${directoryUrl}/directory/v2/namespaces/platform/entries`) 21 | ).json(); 22 | directory = platform.reduce( 23 | (result, entry) => ({ ...result, [entry.urn]: entry.url }), 24 | directory 25 | ); 26 | 27 | try { 28 | const tenant: { urn: string; url: string }[] = await ( 29 | await fetch( 30 | `${directoryUrl}/directory/v2/namespaces/<%= tenant %>/entries` 31 | ) 32 | ).json(); 33 | 34 | directory = tenant.reduce( 35 | (result, entry) => ({ ...result, [entry.urn]: entry.url }), 36 | directory 37 | ); 38 | } catch (err) { 39 | // Tenant directory may not exist if no entries have been added. 40 | } 41 | } 42 | 43 | return { directory, environment }; 44 | } 45 | ); 46 | 47 | export const initialConfigState: ConfigState = { 48 | initialized: false, 49 | environment: {}, 50 | directory: {}, 51 | }; 52 | 53 | const configSlice = createSlice({ 54 | name: CONFIG_FEATURE_KEY, 55 | initialState: initialConfigState, 56 | reducers: {}, 57 | extraReducers: (builder) => { 58 | builder.addCase(initializeConfig.fulfilled, (state, { payload }) => { 59 | state.environment = payload.environment; 60 | state.directory = payload.directory; 61 | state.initialized = true; 62 | }); 63 | }, 64 | }); 65 | 66 | export const configReducer = configSlice.reducer; 67 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/pipeline.ts: -------------------------------------------------------------------------------- 1 | import { formatFiles, generateFiles, Tree } from '@nx/devkit'; 2 | import * as path from 'path'; 3 | import { pipelineEnvs as envs } from '../../pipeline-envs'; 4 | import { getGitRemoteUrl } from '../../utils/git-utils'; 5 | import applyInfraGenerator from '../apply-infra/apply-infra'; 6 | import { NormalizedSchema, Schema } from './schema'; 7 | 8 | function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { 9 | const ocEnvProjects = options.envs?.split(' ') || [options.infra]; 10 | 11 | const envsProjectsSet = new Set(ocEnvProjects); 12 | if (envsProjectsSet.size !== ocEnvProjects.length) { 13 | throw new Error('Each environment must be a unique project.'); 14 | } else if (ocEnvProjects.length > envs.length) { 15 | throw new Error( 16 | `Provided projects must correspond to ${envs.join(', ')} environments.` 17 | ); 18 | } 19 | 20 | return { 21 | ...options, 22 | ocPipelineName: options.pipeline, 23 | ocInfraProject: options.infra, 24 | ocEnvProjects: ocEnvProjects, 25 | applyPipeline: !!options.apply, 26 | pipelineType: options.type === 'jenkins' ? 'jenkins' : 'actions', 27 | }; 28 | } 29 | 30 | function addFiles(host: Tree, options: NormalizedSchema) { 31 | const templateOptions = { 32 | ...options, 33 | sourceRepositoryUrl: getGitRemoteUrl()?.trim(), 34 | envs, 35 | tmpl: '', 36 | }; 37 | 38 | if (options.pipelineType === 'jenkins') { 39 | generateFiles( 40 | host, 41 | path.join(__dirname, 'jenkins'), 42 | `./.openshift`, 43 | templateOptions 44 | ); 45 | } else if (options.pipelineType === 'actions') { 46 | generateFiles( 47 | host, 48 | path.join(__dirname, 'actions/openshift'), 49 | `./.openshift`, 50 | templateOptions 51 | ); 52 | generateFiles( 53 | host, 54 | path.join(__dirname, 'actions/workflows'), 55 | `./.github/workflows`, 56 | templateOptions 57 | ); 58 | } 59 | } 60 | 61 | export default async function (host: Tree, options: Schema) { 62 | const normalizedOptions = normalizeOptions(host, options); 63 | 64 | addFiles(host, normalizedOptions); 65 | await formatFiles(host); 66 | 67 | if (normalizedOptions.applyPipeline) { 68 | await applyInfraGenerator(host); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/express-service/express-service.ts: -------------------------------------------------------------------------------- 1 | import { deploymentGenerator, getAdspConfiguration } from '@abgov/nx-oc'; 2 | import { 3 | addDependenciesToPackageJson, 4 | formatFiles, 5 | generateFiles, 6 | getWorkspaceLayout, 7 | installPackagesTask, 8 | names, 9 | Tree, 10 | } from '@nx/devkit'; 11 | import { Linter } from '@nx/eslint'; 12 | import * as path from 'path'; 13 | import { Schema, NormalizedSchema } from './schema'; 14 | 15 | async function normalizeOptions( 16 | host: Tree, 17 | options: Schema 18 | ): Promise { 19 | const projectName = names(options.name).fileName; 20 | const projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectName}`; 21 | 22 | const adsp = await getAdspConfiguration(host, options); 23 | 24 | return { 25 | ...options, 26 | projectName, 27 | projectRoot, 28 | adsp, 29 | }; 30 | } 31 | 32 | function addFiles(host: Tree, options: NormalizedSchema) { 33 | const templateOptions = { 34 | ...options, 35 | ...options.adsp, 36 | tmpl: '', 37 | }; 38 | generateFiles( 39 | host, 40 | path.join(__dirname, 'files'), 41 | options.projectRoot, 42 | templateOptions 43 | ); 44 | } 45 | 46 | export default async function (host: Tree, options: Schema) { 47 | const normalizedOptions = await normalizeOptions(host, options); 48 | 49 | const { applicationGenerator: initExpress } = await import('@nx/express'); 50 | await initExpress(host, { 51 | ...options, 52 | skipFormat: true, 53 | skipPackageJson: false, 54 | linter: Linter.EsLint, 55 | unitTestRunner: 'jest', 56 | js: false, 57 | directory: `apps/${options.name}`, 58 | }); 59 | 60 | addDependenciesToPackageJson( 61 | host, 62 | { 63 | '@abgov/adsp-service-sdk': '^2.0.0', 64 | dotenv: '^16.0.0', 65 | passport: '^0.6.0', 66 | 'passport-anonymous': '^1.0.1', 67 | }, 68 | { 69 | '@types/passport': '^1.0.9', 70 | '@types/passport-anonymous': '^1.0.3', 71 | } 72 | ); 73 | 74 | addFiles(host, normalizedOptions); 75 | await formatFiles(host); 76 | 77 | await deploymentGenerator(host, { 78 | ...normalizedOptions, 79 | appType: 'node', 80 | project: normalizedOptions.projectName, 81 | }); 82 | 83 | return () => { 84 | installPackagesTask(host); 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/dotnet-service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | deploymentGenerator, 3 | getAdspConfiguration, 4 | hasDependency, 5 | } from '@abgov/nx-oc'; 6 | import { default as appGenerator } from '@nx-dotnet/core/src/generators/app/generator'; 7 | import { default as refGenerator } from '@nx-dotnet/core/src/generators/nuget-reference/generator'; 8 | import { 9 | generateFiles, 10 | getWorkspaceLayout, 11 | installPackagesTask, 12 | names, 13 | Tree, 14 | } from '@nx/devkit'; 15 | import * as path from 'path'; 16 | import { Schema, NormalizedSchema } from './schema'; 17 | 18 | async function normalizeOptions( 19 | host: Tree, 20 | options: Schema 21 | ): Promise { 22 | const projectName = names(options.name).fileName; 23 | const projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectName}`; 24 | 25 | const adsp = await getAdspConfiguration(host, options); 26 | 27 | return { 28 | ...options, 29 | projectName, 30 | projectRoot, 31 | adsp, 32 | }; 33 | } 34 | 35 | function addFiles(host: Tree, options: NormalizedSchema) { 36 | const templateOptions = { 37 | ...options, 38 | ...options.adsp, 39 | tmpl: '', 40 | }; 41 | generateFiles( 42 | host, 43 | path.join(__dirname, 'files'), 44 | options.projectRoot, 45 | templateOptions 46 | ); 47 | } 48 | 49 | export default async function (host: Tree, options: Schema) { 50 | if (!hasDependency(host, '@nx-dotnet/core')) { 51 | throw new Error('nx-dotnet/core is required to generate dotnet service'); 52 | } 53 | 54 | const normalizedOptions = await normalizeOptions(host, options); 55 | 56 | await appGenerator(host, { 57 | name: normalizedOptions.projectName, 58 | template: 'webapi', 59 | language: 'C#', 60 | testTemplate: 'none', 61 | solutionFile: false, 62 | skipSwaggerLib: true, 63 | pathScheme: 'nx', 64 | }); 65 | 66 | await refGenerator(host, { 67 | allowVersionMismatch: false, 68 | project: normalizedOptions.projectName, 69 | packageName: 'Adsp.Sdk', 70 | version: '2.*', 71 | }); 72 | 73 | addFiles(host, normalizedOptions); 74 | 75 | await deploymentGenerator(host, { 76 | ...normalizedOptions, 77 | appType: 'dotnet', 78 | project: normalizedOptions.projectName, 79 | }); 80 | 81 | return async () => { 82 | installPackagesTask(host); 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/mern/mern.ts: -------------------------------------------------------------------------------- 1 | import { 2 | formatFiles, 3 | generateFiles, 4 | getWorkspaceLayout, 5 | installPackagesTask, 6 | names, 7 | offsetFromRoot, 8 | Tree, 9 | } from '@nx/devkit'; 10 | import * as path from 'path'; 11 | import { getAdspConfiguration } from '@abgov/nx-oc'; 12 | import initExpressService from '../express-service/express-service'; 13 | import initReactApp from '../react-app/react-app'; 14 | import { Schema, NormalizedSchema } from './schema'; 15 | 16 | async function normalizeOptions( 17 | host: Tree, 18 | options: Schema 19 | ): Promise { 20 | const name = names(options.name).fileName; 21 | const projectDirectory = name; 22 | const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-'); 23 | const projectRoot = `${getWorkspaceLayout(host).appsDir}/${projectDirectory}`; 24 | const openshiftDirectory = `.openshift/${projectDirectory}`; 25 | 26 | const adsp = await getAdspConfiguration(host, options); 27 | 28 | return { 29 | ...options, 30 | projectName, 31 | projectRoot, 32 | projectDirectory, 33 | openshiftDirectory, 34 | adsp, 35 | }; 36 | } 37 | 38 | function addFiles(host: Tree, options: NormalizedSchema) { 39 | const templateOptions = { 40 | ...options, 41 | ...names(options.name), 42 | offsetFromRoot: offsetFromRoot(options.projectRoot), 43 | template: '', 44 | }; 45 | generateFiles( 46 | host, 47 | path.join(__dirname, 'files'), 48 | options.projectRoot, 49 | templateOptions 50 | ); 51 | generateFiles( 52 | host, 53 | path.join(__dirname, 'openshift'), 54 | `${options.openshiftDirectory}`, 55 | templateOptions 56 | ); 57 | } 58 | 59 | export default async function(host: Tree, options: Schema) { 60 | const normalizedOptions = await normalizeOptions(host, options); 61 | 62 | await initExpressService(host, { 63 | ...normalizedOptions, 64 | name: `${options.name}-service`, 65 | }); 66 | 67 | await initReactApp(host, { 68 | ...normalizedOptions, 69 | name: `${options.name}-app`, 70 | proxy: { 71 | location: '/api/', 72 | proxyPass: `http://${options.name}-service:3333/${options.name}-service/`, 73 | }, 74 | }); 75 | 76 | // Currently no files specific to MERN generator. 77 | // addFiles(host, normalizedOptions); 78 | await formatFiles(host); 79 | 80 | return () => { 81 | installPackagesTask(host); 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-task-list/react-task-list.spec.ts: -------------------------------------------------------------------------------- 1 | import * as utils from '@abgov/nx-oc'; 2 | import { addProjectConfiguration } from '@nx/devkit'; 3 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 4 | import axios from 'axios'; 5 | import { prompt } from 'enquirer'; 6 | 7 | import { Schema } from './schema'; 8 | import generator from './react-task-list'; 9 | import { QueueDefinition } from '../../utils/task'; 10 | 11 | const queueDefinition: QueueDefinition = { 12 | namespace: 'test', 13 | name: 'run-test', 14 | assignerRoles: ['test-assigner'], 15 | workerRoles: ['test-worker'], 16 | }; 17 | 18 | jest.mock('@abgov/nx-oc'); 19 | const utilsMock = utils as jest.Mocked; 20 | utilsMock.getServiceUrls.mockResolvedValue({ 21 | 'urn:ads:platform:tenant-service:v2': 'https://tenant-service/tenant/v2', 22 | 'urn:ads:platform:configuration-service': 'https://configuration-service', 23 | }); 24 | 25 | utilsMock.realmLogin.mockResolvedValue('token'); 26 | utilsMock.selectTenant.mockResolvedValue({ name: 'demo', realm: 'demo' }); 27 | 28 | jest.mock('axios'); 29 | const axiosMock = axios as jest.Mocked; 30 | axiosMock.get.mockResolvedValueOnce({ 31 | data: { configuration: { queues: { 'test:run-test': queueDefinition } } }, 32 | }); 33 | axiosMock.patch.mockResolvedValueOnce({ 34 | data: { 'test:run-test': queueDefinition }, 35 | }); 36 | 37 | jest.mock('enquirer', () => ({ prompt: jest.fn() })); 38 | const promptMock = prompt as jest.Mock; 39 | promptMock 40 | .mockResolvedValueOnce({ definition: 'test:run-test' }) 41 | .mockResolvedValueOnce({ addStream: true }); 42 | 43 | describe('React Task List Generator', () => { 44 | const options: Schema = { 45 | project: 'test', 46 | env: 'dev', 47 | }; 48 | 49 | it('can run', async () => { 50 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 51 | 52 | addProjectConfiguration(host, 'test', { 53 | root: 'apps/test', 54 | projectType: 'application', 55 | targets: { 56 | build: { 57 | executor: '@nx/web:webpack', 58 | }, 59 | }, 60 | }); 61 | 62 | await generator(host, options); 63 | expect(host.exists('apps/test/src/app/run-test/run-test.tsx')).toBeTruthy(); 64 | expect( 65 | host.exists('apps/test/src/app/run-test/run-test.slice.ts') 66 | ).toBeTruthy(); 67 | expect( 68 | host.exists('apps/test/src/app/run-test/run-test.module.css') 69 | ).toBeTruthy(); 70 | }, 30000); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/dotnet-service/files/Program.cs__tmpl__: -------------------------------------------------------------------------------- 1 | using Adsp.Sdk; 2 | using Adsp.Sdk.Examples; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | builder.Services.AddLogging(); 8 | builder.Services.AddControllers(); 9 | 10 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 11 | builder.Services.AddEndpointsApiExplorer(); 12 | builder.Services.AddSwaggerGen(); 13 | 14 | // Initialization of ADSP SDK components... 15 | // Learn more about configuring ADSP SDK at https://govalta.github.io/adsp-monorepo/platform/platform-dotnet-sdk.html 16 | // 1. Bind configuration section or otherwise configure the options. 17 | var adspConfiguration = builder.Configuration.GetSection("Adsp"); 18 | builder.Services.Configure(adspConfiguration); 19 | 20 | // 2. Add ADSP components to the service collection. 21 | builder.Services.AddAdspForService(options => 22 | { 23 | // 3. Provide configuration of the service. 24 | // 3a. Configure basic service information. 25 | var serviceId = AdspId.Parse(adspConfiguration.GetValue("ClientId")); 26 | options.ServiceId = serviceId; 27 | options.DisplayName = "<%= projectName %>"; 28 | 29 | // 3b. Register configuration definition. 30 | options.Configuration = new ConfigurationDefinition( 31 | "Configuration of the hello world sample service.", 32 | (tenant, core) => tenant 33 | ); 34 | // Enable / disable socket.io based configuration invalidation. 35 | // Include `urn:ads:platform:push-service` audience in access token; e.g use an audience mapper on the associated client. 36 | options.EnableConfigurationInvalidation = false; 37 | 38 | // 3c. Register service roles. 39 | options.Roles = new[] { 40 | new ServiceRole { 41 | Role = ServiceRoles.HelloWorlder, 42 | Description = "Hello worlder role that allows user to post a message to the API." 43 | } 44 | }; 45 | 46 | // 3d. Register domain events. 47 | options.Events = new[] { 48 | new DomainEventDefinition( 49 | HelloWorldEvent.EventName, 50 | "Signalled when a hello world message is posted to the API." 51 | ) 52 | }; 53 | }); 54 | 55 | var app = builder.Build(); 56 | 57 | app.UseSwagger(); 58 | app.UseHttpsRedirection(); 59 | 60 | // 4. Add ADSP middleware. 61 | app.UseAdsp(); 62 | app.UseAuthorization(); 63 | app.UseAdspMetadata(new AdspMetadataOptions 64 | { 65 | ApiPath = "WeatherForecast" 66 | }); 67 | 68 | app.MapControllers(); 69 | 70 | app.Run(); 71 | -------------------------------------------------------------------------------- /packages/nx-adsp/src/generators/react-app/files/src/main.tsx__tmpl__: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 4 | import { Provider } from 'react-redux'; 5 | import { 6 | CallbackComponent, 7 | loadUser, 8 | OidcProvider, 9 | SignoutCallbackComponent, 10 | } from 'redux-oidc'; 11 | 12 | import { environment } from './environments/environment'; 13 | import { createUserManager } from './access'; 14 | import App from './app/app'; 15 | import { initializeConfig } from './app/config.slice'; 16 | import { store } from './store'; 17 | 18 | // Fetch configuration from web server; otherwise fallback to static environment. 19 | fetch('/config/config.json') 20 | .then(res => res.ok ? 21 | res.json() : 22 | environment 23 | ) 24 | .then((env) => { 25 | 26 | store.dispatch(initializeConfig(env)); 27 | const userManager = createUserManager(env.access); 28 | loadUser(store, userManager); 29 | 30 | ReactDOM.render( 31 | 32 | 33 | 34 | 35 | 36 | 39 | history.push('/')} 42 | errorCallback={() => history.push('/')} 43 | > 44 | signing in... 45 | 46 | } 47 | /> 48 | 51 | history.push('/')} 54 | errorCallback={() => history.push('/')} 55 | > 56 | signing out... 57 | 58 | } 59 | /> 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | , 68 | document.getElementById('root') 69 | ); 70 | }); 71 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/jenkins/environment.infra.yml__tmpl__: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: ImageStream 6 | metadata: 7 | name: jenkins-agent-node12 8 | namespace: <%= ocInfraProject %> 9 | annotations: 10 | slave-label: node12 11 | labels: 12 | role: jenkins-slave 13 | - apiVersion: v1 14 | kind: BuildConfig 15 | metadata: 16 | name: jenkins-agent-node12 17 | namespace: <%= ocInfraProject %> 18 | spec: 19 | output: 20 | to: 21 | kind: ImageStreamTag 22 | name: jenkins-agent-node12:latest 23 | runPolicy: Serial 24 | source: 25 | contextDir: agent-nodejs-12 26 | git: 27 | uri: 'https://github.com/openshift/jenkins.git' 28 | type: Git 29 | strategy: 30 | dockerStrategy: {} 31 | type: Docker 32 | triggers: 33 | - type: ConfigChange 34 | - apiVersion: v1 35 | kind: ImageStream 36 | metadata: 37 | name: jenkins-agent-node12-dotnet5 38 | namespace: <%= ocInfraProject %> 39 | annotations: 40 | slave-label: node12-dotnet5 41 | labels: 42 | role: jenkins-slave 43 | - apiVersion: v1 44 | kind: BuildConfig 45 | metadata: 46 | name: jenkins-agent-node12-dotnet5 47 | namespace: <%= ocInfraProject %> 48 | spec: 49 | output: 50 | to: 51 | kind: ImageStreamTag 52 | name: jenkins-agent-node12-dotnet5:latest 53 | runPolicy: Serial 54 | source: 55 | dockerfile: | 56 | FROM quay.io/openshift/origin-jenkins-agent-base:v4.0 57 | 58 | USER 0 59 | 60 | RUN rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm && \ 61 | DISABLES="--disablerepo=rhel-server-extras --disablerepo=rhel-server --disablerepo=rhel-fast-datapath --disablerepo=rhel-server-optional --disablerepo=rhel-server-ose --disablerepo=rhel-server-rhscl" && \ 62 | yum $DISABLES install -y --setopt=tsflags=nodocs --disableplugin=subscription-manager dotnet-sdk-5.0 && \ 63 | yum clean all -y 64 | 65 | USER 1001 66 | type: Binary 67 | strategy: 68 | dockerStrategy: 69 | from: 70 | kind: ImageStreamTag 71 | name: jenkins-agent-node12:latest 72 | type: Docker 73 | - apiVersion: v1 74 | kind: BuildConfig 75 | metadata: 76 | name: <%= ocPipelineName %> 77 | namespace: <%= ocInfraProject %> 78 | spec: 79 | runPolicy: Serial 80 | source: 81 | git: 82 | uri: '<%= sourceRepositoryUrl %>' 83 | type: Git 84 | strategy: 85 | jenkinsPipelineStrategy: 86 | jenkinsfilePath: .openshift/Jenkinsfile 87 | type: JenkinsPipeline 88 | -------------------------------------------------------------------------------- /packages/nx-release/src/release-plugin/wrap-plugin.spec.ts: -------------------------------------------------------------------------------- 1 | import type { Commit, VerifyReleaseContext } from 'semantic-release'; 2 | import { mocked } from 'jest-mock'; 3 | import { wrapPlugin } from './wrap-plugin'; 4 | import { getProjectChangePaths } from './nx-util'; 5 | import { getPathCommitHashes } from './git-utils'; 6 | 7 | jest.mock('./nx-util'); 8 | const mockedGetProjectChangePaths = mocked(getProjectChangePaths); 9 | jest.mock('./git-utils'); 10 | const mockedGetPathCommitHashes = mocked(getPathCommitHashes); 11 | 12 | describe('wrapPlugin', () => { 13 | beforeEach(() => { 14 | mockedGetProjectChangePaths.mockReset(); 15 | mockedGetPathCommitHashes.mockReset(); 16 | }); 17 | 18 | it('can wrap plugin function', () => { 19 | const wrapped = wrapPlugin(jest.fn()); 20 | expect(wrapped).toBeTruthy(); 21 | expect(typeof wrapped).toBe('function'); 22 | }); 23 | 24 | it('can filter commits', async () => { 25 | const plugin = jest.fn(); 26 | plugin.mockReturnValue('result'); 27 | jest.mock('./git-utils'); 28 | const wrapped = wrapPlugin(plugin); 29 | 30 | mockedGetProjectChangePaths.mockReturnValue(Promise.resolve(['test1'])); 31 | 32 | mockedGetPathCommitHashes.mockReturnValue(Promise.resolve(['test1'])); 33 | 34 | const logger = { 35 | log: jest.fn(), 36 | error: jest.fn(), 37 | }; 38 | 39 | const result = await wrapped( 40 | { project: 'test', config: 'config' }, 41 | { 42 | commits: [ 43 | { commit: { long: 'test1', short: 'test1' } } as Commit, 44 | { commit: { long: 'test2', short: 'test2' } } as Commit, 45 | ], 46 | logger, 47 | env: {}, 48 | cwd: '', 49 | stderr: null, 50 | stdout: null, 51 | } as unknown as VerifyReleaseContext 52 | ); 53 | 54 | expect(result).toBe('result'); 55 | expect(plugin.mock.calls.length).toBe(1); 56 | expect(plugin.mock.calls[0][0].config).toBe('config'); 57 | expect(plugin.mock.calls[0][1].commits.length).toBe(1); 58 | expect(plugin.mock.calls[0][1].commits[0].commit.long).toBe('test1'); 59 | }); 60 | 61 | it('can skip filtering if no project configured', async () => { 62 | const plugin = jest.fn(); 63 | plugin.mockReturnValue('result'); 64 | const wrapped = wrapPlugin(plugin); 65 | 66 | const logger = { 67 | log: jest.fn(), 68 | error: jest.fn(), 69 | }; 70 | 71 | const result = await wrapped( 72 | { project: null, config: 'config' }, 73 | { 74 | commits: [ 75 | { commit: { long: 'test1', short: 'test1' } } as Commit, 76 | { commit: { long: 'test2', short: 'test2' } } as Commit, 77 | ], 78 | logger, 79 | env: {}, 80 | cwd: '', 81 | } as unknown as VerifyReleaseContext 82 | ); 83 | 84 | expect(result).toBe('result'); 85 | expect(plugin.mock.calls.length).toBe(1); 86 | expect(plugin.mock.calls[0][1].commits.length).toBe(2); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [main, beta] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [main, beta] 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze 24 | runs-on: ubuntu-latest 25 | permissions: 26 | actions: read 27 | contents: read 28 | security-events: write 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | language: ["javascript", "typescript"] 34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 35 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v3 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v2 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | 50 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 51 | # queries: security-extended,security-and-quality 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v2 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 60 | 61 | # If the Autobuild fails above, remove it and uncomment the following three lines. 62 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 63 | 64 | # - run: | 65 | # echo "Run, Build Application using script" 66 | # ./location_of_script_within_repo/buildscript.sh 67 | 68 | - name: Perform CodeQL Analysis 69 | uses: github/codeql-action/analyze@v2 70 | -------------------------------------------------------------------------------- /packages/nx-oc/src/generators/pipeline/pipeline.spec.ts: -------------------------------------------------------------------------------- 1 | import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; 2 | import { Schema } from './schema'; 3 | import generator from './pipeline'; 4 | 5 | describe('Pipeline Generator', () => { 6 | describe('Jenkins', () => { 7 | const options: Schema = { 8 | pipeline: 'test', 9 | type: 'jenkins', 10 | infra: 'test-infra', 11 | envs: 'test-dev', 12 | }; 13 | 14 | it('can run', async () => { 15 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 16 | await generator(host, options); 17 | expect(host.exists('.openshift/Jenkinsfile')).toBeTruthy(); 18 | expect(host.exists('.openshift/environment.infra.yml')).toBeTruthy(); 19 | expect(host.exists('.openshift/environments.yml')).toBeTruthy(); 20 | }); 21 | 22 | it('can generate multiple envs', async () => { 23 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 24 | await generator(host, { ...options, envs: 'test-dev test-test' }); 25 | expect(host.exists('.openshift/environments.yml')).toBeTruthy(); 26 | 27 | const envs = host.read('.openshift/environments.yml').toString(); 28 | expect(envs).toContain('test-dev'); 29 | expect(envs).toContain('test-test'); 30 | }); 31 | 32 | it('can fail for duplicate env project', async () => { 33 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 34 | await expect( 35 | generator(host, { ...options, envs: 'test-dev test-dev' }) 36 | ).rejects.toThrow('Each environment must be a unique project.'); 37 | }); 38 | }); 39 | 40 | describe('GitHub Actions', () => { 41 | const options: Schema = { 42 | pipeline: 'test', 43 | type: 'actions', 44 | infra: 'test-infra', 45 | envs: 'test-dev', 46 | }; 47 | 48 | it('can run', async () => { 49 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 50 | await generator(host, options); 51 | expect(host.exists('.github/workflows/pipeline.yml')).toBeTruthy(); 52 | expect(host.exists('.openshift/environment.infra.yml')).toBeTruthy(); 53 | expect(host.exists('.openshift/environments.yml')).toBeTruthy(); 54 | }); 55 | 56 | it('can generate multiple envs', async () => { 57 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 58 | await generator(host, { ...options, envs: 'test-dev test-test' }); 59 | expect(host.exists('.openshift/environments.yml')).toBeTruthy(); 60 | 61 | const envs = host.read('.openshift/environments.yml').toString(); 62 | expect(envs).toContain('test-dev'); 63 | expect(envs).toContain('test-test'); 64 | }); 65 | 66 | it('can fail for duplicate env project', async () => { 67 | const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); 68 | await expect( 69 | generator(host, { ...options, envs: 'test-dev test-dev' }) 70 | ).rejects.toThrow('Each environment must be a unique project.'); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@abgov/nx-tools", 3 | "version": "0.0.0", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "nx": "nx", 7 | "start": "nx serve", 8 | "build": "nx build", 9 | "test": "nx test", 10 | "lint": "nx workspace-lint && nx lint", 11 | "e2e": "nx e2e", 12 | "affected:apps": "nx affected:apps", 13 | "affected:libs": "nx affected:libs", 14 | "affected:build": "nx affected:build", 15 | "affected:e2e": "nx affected:e2e", 16 | "affected:test": "nx affected:test", 17 | "affected:lint": "nx affected:lint", 18 | "affected:dep-graph": "nx affected:dep-graph", 19 | "affected": "nx affected", 20 | "format": "nx format:write", 21 | "format:write": "nx format:write", 22 | "format:check": "nx format:check", 23 | "update": "nx migrate latest", 24 | "workspace-generator": "nx workspace-generator", 25 | "dep-graph": "nx dep-graph", 26 | "help": "nx help" 27 | }, 28 | "private": true, 29 | "dependencies": { 30 | "@angular/core": "19.1.6", 31 | "@nx/devkit": "20.4.2", 32 | "json-schema-to-typescript": "^13.0.1", 33 | "react": "18.3.1", 34 | "react-dom": "18.3.1", 35 | "react-is": "18.3.1" 36 | }, 37 | "devDependencies": { 38 | "@abgov/nx-release": "^8.1.4", 39 | "@angular-devkit/build-angular": "19.1.7", 40 | "@angular-devkit/core": "19.1.7", 41 | "@angular-devkit/schematics": "19.1.7", 42 | "@nx-dotnet/core": "^2.5.0", 43 | "@nx/angular": "20.4.2", 44 | "@nx/cypress": "20.4.2", 45 | "@nx/eslint": "20.4.2", 46 | "@nx/eslint-plugin": "20.4.2", 47 | "@nx/express": "20.4.2", 48 | "@nx/jest": "20.4.2", 49 | "@nx/js": "20.4.2", 50 | "@nx/node": "20.4.2", 51 | "@nx/plugin": "20.4.2", 52 | "@nx/react": "20.4.2", 53 | "@nx/web": "20.4.2", 54 | "@nx/workspace": "20.4.2", 55 | "@schematics/angular": "19.1.7", 56 | "@semantic-release/commit-analyzer": "^11.1.0", 57 | "@semantic-release/release-notes-generator": "^12.1.0", 58 | "@types/jest": "29.5.13", 59 | "@types/node": "^18.16.9", 60 | "@types/semantic-release": "^20.0.6", 61 | "@types/simple-oauth2": "^4.1.1", 62 | "@types/split2": "^4.2.3", 63 | "@types/stream-buffers": "^3.0.3", 64 | "@typescript-eslint/eslint-plugin": "7.18.0", 65 | "@typescript-eslint/parser": "7.18.0", 66 | "@typescript-eslint/utils": "^8.13.0", 67 | "axios": "^1.6.0", 68 | "dotenv": "10.0.0", 69 | "enquirer": "^2.3.6", 70 | "eslint": "8.57.1", 71 | "eslint-config-prettier": "9.1.0", 72 | "execa": "^5.0.0", 73 | "get-stream": "^6.0.0", 74 | "jest": "29.7.0", 75 | "json-schema-to-typescript": "^13.0.1", 76 | "nx": "20.4.2", 77 | "open": "^8.4.0", 78 | "prettier": "2.7.1", 79 | "semantic-release": "^23.0.2", 80 | "simple-oauth2": "^5.0.0", 81 | "split2": "^4.2.0", 82 | "stream-buffers": "^3.0.2", 83 | "ts-jest": "^29.1.2", 84 | "ts-node": "10.9.1", 85 | "tslib": "^2.0.0", 86 | "typescript": "5.7.3", 87 | "yaml": "~2.2.2" 88 | }, 89 | "overrides": { 90 | "@angular-devkit/build-angular": "19.1.7", 91 | "@angular/compiler-cli": "19.1.6", 92 | "@nx/devkit": "20.4.2", 93 | "semantic-release": "^23.0.2" 94 | } 95 | } 96 | --------------------------------------------------------------------------------