├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── build.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .versionrc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── index.ts ├── package-lock.json ├── package.json ├── public_api.ts ├── renovate.json ├── schematics ├── collection.json ├── ng-add │ ├── files │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ └── nginx.conf │ ├── index.ts │ ├── schema.d.ts │ └── schema.json └── utils.ts ├── src ├── builders.json ├── deploy │ ├── actions.ts │ ├── builder.ts │ ├── schema.d.ts │ └── schema.json ├── engine │ └── engine.ts └── interfaces.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended" 10 | ], 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly" 14 | }, 15 | "parser": "@typescript-eslint/parser", 16 | "parserOptions": { 17 | "ecmaVersion": 2018 18 | }, 19 | "plugins": ["@typescript-eslint"], 20 | "rules": {} 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## I'm submitting a... 8 | 9 | 10 |

11 | [ ] Regression (a behavior that used to work and stopped working in a new release)
12 | [ ] Bug report  
13 | [ ] Performance issue
14 | [ ] Feature request
15 | [ ] Documentation issue or request
16 | [ ] Support request
17 | [ ] Other... Please describe:
18 | 
19 | 20 | ## Current behavior 21 | 22 | 23 | 24 | ## Expected behavior 25 | 26 | 27 | 28 | ## Minimal reproduction of the problem with instructions 29 | 30 | For bug reports please provide the _STEPS TO REPRODUCE_ and if possible a _MINIMAL DEMO_ of the problem. 31 | 32 | ## What is the motivation / use case for changing the behavior? 33 | 34 | 35 | 36 | ## Environment 37 | 38 |

39 | - Node version: XX  
40 | - Git version
41 | - Platform:  
42 | 
43 | Others:
44 | 
45 | 
46 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | Please check if your PR fulfills the following requirements: 4 | 5 | - [ ] The commit message follows our [guidelines](https://github.com/kauppfbi/ngx-deploy-docker/blob/master/CONTRIBUTING.md) 6 | - [ ] Tests for the changes have been added (for bug fixes / features) 7 | - [ ] Docs have been added / updated (for bug fixes / features) 8 | 9 | ## PR Type 10 | 11 | What kind of change does this PR introduce? 12 | 13 | 14 | 15 | ``` 16 | [ ] Bugfix 17 | [ ] Feature 18 | [ ] Code style update (formatting, local variables) 19 | [ ] Refactoring (no functional changes, no api changes) 20 | [ ] Build related changes 21 | [ ] CI related changes 22 | [ ] Documentation content changes 23 | [ ] Other... Please describe: 24 | ``` 25 | 26 | ## What is the current behavior? 27 | 28 | 29 | 30 | Issue Number: N/A 31 | 32 | ## What is the new behavior? 33 | 34 | ## Does this PR introduce a breaking change? 35 | 36 | ``` 37 | [ ] Yes 38 | [ ] No 39 | ``` 40 | 41 | 42 | 43 | ## Other information 44 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: CI Pipeline 5 | 6 | # Trigger the workflow on push or pull request 7 | on: push 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [14.x, 16.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: Install Dependencies 24 | uses: bahmutov/npm-install@v1 25 | - name: Run Snyk to check for vulnerabilities 26 | uses: snyk/actions/node@master 27 | env: 28 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 29 | with: 30 | command: monitor 31 | - name: Check Format 32 | run: npm run format:test 33 | - name: Run Tests 34 | run: npm test 35 | - name: Build Package 36 | run: npm run build 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tmp 3 | .sass-cache 4 | .DS_Store 5 | .bash_history 6 | *.swp 7 | *.swo 8 | 9 | *.classpath 10 | *.project 11 | *.settings/ 12 | 13 | .vim/bundle 14 | nvim/autoload 15 | nvim/plugged 16 | nvim/doc 17 | nvim/swaps 18 | nvim/colors 19 | dist 20 | 21 | /src/mini-testdrive/404.html 22 | /src/mini-testdrive/CNAME 23 | .angulardoc.json 24 | .vscode/settings.json 25 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | dist -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "releaseCommitMessageFormat": "chore(release): 🚀 {{currentTag}}", 3 | "types": [ 4 | { "type": "feat", "section": "🎸 Features" }, 5 | { "type": "fix", "section": "🐛 Bug Fixes" }, 6 | { "type": "docs", "section": "✏️ Documentation" }, 7 | { "type": "chore", "hidden": true }, 8 | { "type": "perf", "hidden": true }, 9 | { "type": "style", "hidden": true }, 10 | { "type": "refactor", "hidden": true }, 11 | { "type": "test", "hidden": true } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.1.0](https://github.com/kauppfbi/ngx-deploy-docker/compare/v0.0.5...v0.1.0) (2021-11-22) 6 | 7 | ### ⚠ BREAKING CHANGES 8 | 9 | - Drop Support for Angular versions <13 10 | - Drop Support for nodejs v10 and v12 11 | - option `build-target` replaces `configuration` 12 | 13 | ### ✏️ Documentation 14 | 15 | - add ci pipeline badge ([ae9bb2f](https://github.com/kauppfbi/ngx-deploy-docker/commit/ae9bb2faf13271bcf8f0874087ead778ab62e7f9)) 16 | 17 | ### 🎸 Features 18 | 19 | - support angular v13 ([#104](https://github.com/kauppfbi/ngx-deploy-docker/issues/104)) ([abf319a](https://github.com/kauppfbi/ngx-deploy-docker/commit/abf319aec82bdf1646f16192e24d3d90e23a4d1c)) 20 | 21 | ### 0.0.5 (2019-12-30) 22 | 23 | ### 🐛 Bug Fixes 24 | 25 | - update ng-add schematic ([caa6b6c](https://github.com/kauppfbi/ngx-deploy-docker/commit/caa6b6c798235b053917fc87391925f32cf6d129)) 26 | 27 | ### ✏️ Documentation 28 | 29 | - update prerequisites ([5d9e116](https://github.com/kauppfbi/ngx-deploy-docker/commit/5d9e116648b7db781fcb590e9638f2874730141e)) 30 | 31 | ### 0.0.4 (2019-10-04) 32 | 33 | ### 🐛 Bug Fixes 34 | 35 | - use correct path to read from package.json ([5b1b8a6](https://github.com/kauppfbi/ngx-deploy-docker/commit/5b1b8a6)) 36 | 37 | ### 0.0.3 (2019-10-03) 38 | 39 | ### ✏️ Documentation 40 | 41 | - improve readme & project documentation ([36e45be](https://github.com/kauppfbi/ngx-deploy-docker/commit/36e45be)) 42 | 43 | ### 0.0.2 (2019-10-02) 44 | 45 | ### 🎸 Features 46 | 47 | - 🎉 implement ng-add schematic for the package ([d45bf1f](https://github.com/kauppfbi/ngx-deploy-docker/commit/d45bf1f)) 48 | - add docker build and push logic to builder ([2ac677b](https://github.com/kauppfbi/ngx-deploy-docker/commit/2ac677b)) 49 | - add docker options in builder ([8da80cb](https://github.com/kauppfbi/ngx-deploy-docker/commit/8da80cb)) 50 | - autogenerate dockerfile and nginx config if not present ([6edcd86](https://github.com/kauppfbi/ngx-deploy-docker/commit/6edcd86)) 51 | 52 | ### 🐛 Bug Fixes 53 | 54 | - adjust copy command to also copy .dockerignore file ([1baf9bf](https://github.com/kauppfbi/ngx-deploy-docker/commit/1baf9bf)) 55 | - set correct builder in ng add schematic ([55cd271](https://github.com/kauppfbi/ngx-deploy-docker/commit/55cd271)) 56 | - use correct image name with account and tag ([dcb14b3](https://github.com/kauppfbi/ngx-deploy-docker/commit/dcb14b3)) 57 | 58 | ### 0.0.1 (2019-09-30) 59 | 60 | ### ✏️ Documentation 61 | 62 | - add code of conduct ([187062e](https://github.com/kauppfbi/ngx-deploy-docker/commit/187062e)) 63 | - add contributing guide ([c4db8b3](https://github.com/kauppfbi/ngx-deploy-docker/commit/c4db8b3)) 64 | - add first draft for README ([cb490bd](https://github.com/kauppfbi/ngx-deploy-docker/commit/cb490bd)) 65 | - add github templates for issues and prs ([7c771c1](https://github.com/kauppfbi/ngx-deploy-docker/commit/7c771c1)) 66 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Our Pledge 2 | 3 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 4 | 5 | ## Our Standards 6 | 7 | ### Examples of behavior that contributes to creating a positive environment include: 8 | 9 | - Using welcoming and inclusive language 10 | - Being respectful of differing viewpoints and experiences 11 | - Gracefully accepting constructive criticism 12 | - Focusing on what is best for the community 13 | - Showing empathy towards other community members 14 | - Examples of unacceptable behavior by participants include: 15 | 16 | ### The use of sexualized language or imagery and unwelcome sexual attention or advances 17 | 18 | - Trolling, insulting/derogatory comments, and personal or political attacks 19 | - Public or private harassment 20 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission 21 | - Other conduct which could reasonably be considered inappropriate in a professional setting 22 | 23 | ## Our Responsibilities 24 | 25 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 26 | 27 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 28 | 29 | ## Scope 30 | 31 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 32 | 33 | ## Enforcement 34 | 35 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [INSERT EMAIL ADDRESS]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 36 | 37 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. 38 | 39 | ## Attribution 40 | 41 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ngx-deploy-docker 2 | 3 | 🙏 We would ❤️ for you to contribute to `ngx-deploy-docker` and help make it better! 4 | 5 | ## Getting started 6 | 7 | ### 1. Angular CLI 8 | 9 | 1. Install the next version of the Angular CLI. 10 | 11 | ```sh 12 | npm install -g @angular/cli 13 | ``` 14 | 15 | 2. Run `ng version`, make sure you have installed Angular CLI v8.3.0 or greater. 16 | 17 | 3. Update your existing project using the command: 18 | 19 | ```sh 20 | ng update @angular/cli @angular/core 21 | ``` 22 | 23 | ### 2. npm link 24 | 25 | Use the following instructions to make `ngx-deploy-docker` available locally via `npm link`. 26 | 27 | 1. Clone the project 28 | 29 | ```sh 30 | git clone https://github.com/kauppfbi/ngx-deploy-docker.git 31 | cd ngx-deploy-docker 32 | ``` 33 | 34 | 2. Install the dependencies 35 | 36 | ```sh 37 | npm install 38 | ``` 39 | 40 | 3. Build the project: 41 | 42 | ```sh 43 | npm run build 44 | ``` 45 | 46 | 4. Create a local npm link: 47 | 48 | ```sh 49 | cd dist 50 | npm link 51 | ``` 52 | 53 | Read more about the `link` feature in the [official NPM documentation](https://docs.npmjs.com/cli/link). 54 | 55 | ### 3. Adding to an Angular project -- ng add 56 | 57 | Once you have completed the previous steps to `npm link` the local copy of `ngx-deploy-docker`, follow these steps to use it in a local Angular project. 58 | 59 | 1. Enter the project directory 60 | 61 | ```sh 62 | cd your-angular-project 63 | ``` 64 | 65 | 2. Add the local version of `ngx-deploy-docker`. 66 | 67 | ```sh 68 | npm link ngx-deploy-docker 69 | ``` 70 | 71 | 3. Now execute the `ng-add` schematic. 72 | 73 | ```sh 74 | ng add ngx-deploy-docker 75 | ``` 76 | 77 | 4. You can now deploy your angular app to a Docker Registry. 78 | 79 | ```sh 80 | ng deploy 81 | ``` 82 | 83 | 5. You can remove the link later by running `npm unlink` 84 | 85 | ### 4. Testing 86 | 87 | Testing is done with [Jest](https://jestjs.io/). 88 | To run the tests: 89 | 90 | ```sh 91 | npm test 92 | ``` 93 | 94 | ## Contribute 95 | 96 | ### Coding Rules 97 | 98 | To ensure consistency throughout the source code, keep these rules in mind as you are working: 99 | 100 | - All features or bug fixes **must be tested** by one or more specs (unit-tests). 101 | - All public API methods **must be documented**. 102 | 103 | ### Commit Message Guidelines 104 | 105 | We have very precise rules over how our git commit messages can be formatted. This leads to **more 106 | readable messages** that are easy to follow when looking through the **project history**. But also, 107 | we use the git commit messages to **generate the changelog**. 108 | 109 | #### Commit Message Format 110 | 111 | The format of commit messages must align with the rules provided by [@commitlint/config-conventional](https://www.npmjs.com/package/@commitlint/config-conventional) 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - 2021 Fabian Kaupp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngx-deploy-docker 🚀🐳 2 | 3 | **This Project is still work in progress.** 4 | 5 | ![CI Pipeline](https://github.com/kauppfbi/ngx-deploy-docker/workflows/CI%20Pipeline/badge.svg) 6 | [![NPM version][npm-image]][npm-url] 7 | [![The MIT License](https://img.shields.io/badge/license-MIT-orange.svg?color=blue&style=flat-square)](http://opensource.org/licenses/MIT) 8 | 9 | **Dockerize your Angular application with ease and deploy your image right from the Angular CLI to a registry 🚀** 10 | 11 | **Table of contents:** 12 | 13 | 1. [📖 Changelog](#changelog) 14 | 2. [🚀 Quick Start (local development)](#quickstart-local) 15 | 3. [🚀 Continuous Delivery](#continuous-delivery) 16 | 4. [📦 Options](#options) 17 | - [--base-href](#base-href) 18 | - [--build-target](#build-target) 19 | - [--no-build](#no-build) 20 | - [--image-name](#image-name) 21 | - [--account](#account) 22 | - [--tag](#tag) 23 | 5. [📁 Configuration File](#configuration-file) 24 | 6. [🏁 Next milestones](#milestones) 25 | 26 |
27 | 28 | ## 📖 Changelog 29 | 30 | A detailed changelog is available [here](https://github.com/kauppfbi/ngx-deploy-docker/blob/master/CHANGELOG.md). 31 | 32 | ## 🚀 Quick Start (local development) 33 | 34 | This quick start assumes that you are starting from scratch. 35 | If you already have an existing Angular project, skip step 1. 36 | 37 | 1. Install the latest version of the **`Angular CLI` (v13.0.0 or greater) globally** 38 | and create a new Angular project. Make sure you have a suitable version of `nodeJs` installed. 39 | 40 | ```sh 41 | npm install -g @angular/cli 42 | ng new your-angular-project --defaults 43 | cd your-angular-project 44 | ``` 45 | 46 | 2. Add `ngx-deploy-docker` to your project. 47 | 48 | ```sh 49 | ng add ngx-deploy-docker 50 | ``` 51 | 52 | 3. Make sure, Docker works properly on your client and you are authenticated at the repository of your choice. 53 | 54 | ```sh 55 | docker login 56 | ``` 57 | 58 | 4. Deploy your newly built image to the registry with all default settings. 59 | Your project will be automatically built in production mode. 60 | 61 | ```sh 62 | ng deploy 63 | ``` 64 | 65 | Which is the same as: 66 | 67 | ```sh 68 | ng deploy your-angular-project 69 | ``` 70 | 71 | ## 🚀 Continuous Delivery 72 | 73 | ...more to come 74 | 75 | ## 📦 Options 76 | 77 | #### --base-href 78 | 79 | - **optional** 80 | - Default: `undefined` (string) 81 | - Example: 82 | - `ng deploy` – The tag `` remains unchanged in your `index.html` 83 | - `ng deploy --base-href=/XXX/` – The tag `` is added to your `index.html` 84 | 85 | Specifies the base URL for the application being built. 86 | Same as `ng build --base-href=/XXX/` 87 | 88 | #### --build-target 89 | 90 | - **optional** 91 | - Example: 92 | - `ng deploy` – Angular project is build in production mode 93 | - `ng deploy --build-target=:build:test` – Angular project is using the configuration `test` (this configuration must exist in the `angular.json` file) 94 | 95 | A named build target, as specified in the `configurations` section of `angular.json`. 96 | Each named target is accompanied by a configuration of option defaults for that target. 97 | Same as `ng run `. 98 | This command has no effect if the option `--no-build` option is active. 99 | 100 | #### --no-build 101 | 102 | - **optional** 103 | - Default: `false` (boolean) 104 | - Example: 105 | - `ng deploy` – Angular project is build in production mode before the deployment 106 | - `ng deploy --no-build` – Angular project is NOT build 107 | 108 | Skip build process during deployment. 109 | This can be used when you are sure that you haven't changed anything and want to deploy with the latest artifact. 110 | This command causes the `--build-target` setting to have no effect. 111 | 112 | #### --image-name 113 | 114 | - **optional** 115 | - Example: 116 | - `ng deploy` – Docker Image is build with the name of the project as image name 117 | - `ng deploy --image-name=your-special-name` – Docker Image is built with the name provided. 118 | 119 | #### --account 120 | 121 | - **optional** 122 | - Alias: `-a` 123 | - Default: `` (string) 124 | - Example: 125 | - `ng deploy` – Docker Image name is **not** prefixed. 126 | - `ng deploy --account=test` – Docker image name is prefixed with the provided account, like `account/image-name`. 127 | 128 | > This option may be necessary, depending on your write-rights within the repository, you want to push to. 129 | 130 | #### --tag 131 | 132 | - **optional** 133 | - Alias: `-t` 134 | - Default: `latest` (string) 135 | - Example: 136 | - `ng deploy` – Docker Image is build with the tag `latest`, e.g.`account/image-name:latest` 137 | - `ng deploy --tag=v1.0.0` – Docker Image is build with the tag `v1.0.0` 138 | 139 | ## 📁 Configuration File 140 | 141 | To avoid all these command-line cmd options, you can write down your configuration in the `angular.json` file in the `options` attribute of your deploy project's architect. Just change the kebab-case to lower camel case. This is the notation of all options in lower camel case: 142 | 143 | - baseHref 144 | - buildTarget 145 | - noBuild 146 | - imageName 147 | - account 148 | - tag 149 | 150 | A list of all available options is also available [here](https://github.com/kauppfbi/ngx-deploy-docker/blob/master/src/deploy/schema.json). 151 | 152 | Example: 153 | 154 | ```sh 155 | ng deploy --build-target=:build:production --tag=next 156 | ``` 157 | 158 | becomes 159 | 160 | ```json 161 | "deploy": { 162 | "builder": "ngx-deploy-docker:deploy", 163 | "options": { 164 | "buildTarget": ":build:production", 165 | "tag": "next" 166 | } 167 | } 168 | ``` 169 | 170 | And just run `ng deploy` 😄. 171 | 172 | ## 🏁 Next milestones 173 | 174 | - Setup of CI/CD Pipeline for the project 175 | - Code Restructuring: 176 | - Modularization of Schematics and Builders 177 | - Use what you need 178 | - Testing, Testing, Testing: 179 | - Manual tests on different clients with different OS 180 | - Unit and Integration Tests 181 | - Add more options to the deploy builder, what do you need? 182 | - Integration in NxWorkspace 183 | - 💅 Kubernetes deployment right from the CLI 184 | - preparing examples of `how to use the package in CI environment with different Providers for private registries` 185 | - your feature that's not on the list yet? 186 | 187 | We look forward to any help. PRs are welcome! 😃 188 | 189 | ## License 190 | 191 | Code released under the [MIT license](LICENSE). 192 | 193 |
194 | 195 | # Attribution 196 | 197 | ## 🚀 Powered By [ngx-deploy-starter](https://github.com/angular-schule/ngx-deploy-starter) 198 | 199 | ## 🔥 Many things have been taken over from [transloco](https://github.com/ngneat/transloco) 200 | 201 | [npm-url]: https://www.npmjs.com/package/ngx-deploy-docker 202 | [npm-image]: https://badge.fury.io/js/ngx-deploy-docker.svg 203 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | }; 4 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from 'public_api'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-deploy-docker", 3 | "version": "0.1.0", 4 | "description": "Publish your angular projects to a docker registry by just run `ng deploy your-app`", 5 | "scripts": { 6 | "format:write": "prettier **/*.{ts,json,md} --write", 7 | "format:test": "prettier **/*.{ts,json,md} --list-different", 8 | "test": "", 9 | "release": "standard-version", 10 | "build": "rimraf dist && npx tsc && copyfiles -a package.json README.md schematics/**/{collection.json,schema.json,files/**} src/**/{builders.json,schema.json} dist", 11 | "build:watch": "tsc --watch", 12 | "build:test": "" 13 | }, 14 | "schematics": "./schematics/collection.json", 15 | "builders": "./src/builders.json", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/kauppfbi/ngx-deploy-docker.git" 19 | }, 20 | "keywords": [ 21 | "angular", 22 | "cli", 23 | "angular-cli", 24 | "deploy", 25 | "ng-deploy", 26 | "ng deploy", 27 | "docker", 28 | "publish" 29 | ], 30 | "author": { 31 | "name": "Fabian Kaupp", 32 | "email": "kauppfbi@gmail.com" 33 | }, 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/kauppfbi/ngx-deploy-docker/issues" 37 | }, 38 | "homepage": "https://github.com/kauppfbi/ngx-deploy-docker#readme", 39 | "devDependencies": { 40 | "@angular-devkit/architect": "0.1300.3", 41 | "@angular-devkit/core": "13.0.3", 42 | "@angular-devkit/schematics": "13.0.3", 43 | "@commitlint/cli": "8.3.5", 44 | "@commitlint/config-conventional": "8.3.4", 45 | "@types/node": "16.11.9", 46 | "@typescript-eslint/eslint-plugin": "2.26.0", 47 | "@typescript-eslint/parser": "2.26.0", 48 | "copyfiles": "^2.4.1", 49 | "eslint": "6.8.0", 50 | "eslint-config-airbnb-base": "14.1.0", 51 | "eslint-plugin-import": "2.20.1", 52 | "eslint-plugin-node": "11.1.0", 53 | "husky": "4.2.3", 54 | "prettier": "2.0.2", 55 | "pretty-quick": "2.0.1", 56 | "rimraf": "3.0.2", 57 | "standard-version": "7.1.0", 58 | "typescript": "4.4.4" 59 | }, 60 | "peerDependencies": { 61 | "@angular-devkit/architect": ">=0.1300.0", 62 | "@angular-devkit/core": ">=13.0.0", 63 | "@angular-devkit/schematics": ">=13.0.0" 64 | }, 65 | "husky": { 66 | "hooks": { 67 | "pre-commit": "pretty-quick --staged", 68 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './schematics/ng-add'; 2 | export * from './src/deploy/actions'; 3 | export * from './src/deploy/builder'; 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"] 3 | } 4 | -------------------------------------------------------------------------------- /schematics/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", 3 | "schematics": { 4 | "ng-add": { 5 | "description": "Add Docker Registry Publish schematic (ngx-deploy-docker)", 6 | "factory": "./ng-add/index#ngAdd", 7 | "schema": "./ng-add/schema.json", 8 | "aliases": ["install"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schematics/ng-add/files/.dockerignore: -------------------------------------------------------------------------------- 1 | e2e 2 | node_modules 3 | src -------------------------------------------------------------------------------- /schematics/ng-add/files/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | LABEL version="<%= version %>" 3 | 4 | COPY nginx.conf /etc/nginx/nginx.conf 5 | 6 | WORKDIR /usr/share/nginx/html 7 | COPY <%= outputPath %> . -------------------------------------------------------------------------------- /schematics/ng-add/files/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | server { 9 | listen 80; 10 | server_name localhost; 11 | 12 | root /usr/share/nginx/html; 13 | index index.html index.htm; 14 | include /etc/nginx/mime.types; 15 | 16 | gzip on; 17 | gzip_min_length 1000; 18 | gzip_proxied expired no-cache no-store private auth; 19 | gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; 20 | 21 | location / { 22 | try_files $uri $uri/ /index.html; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /schematics/ng-add/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Rule, 3 | SchematicContext, 4 | Tree, 5 | SchematicsException, 6 | url, 7 | chain, 8 | apply, 9 | mergeWith, 10 | template, 11 | } from '@angular-devkit/schematics'; 12 | import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; 13 | 14 | import { 15 | addPackageToPackageJson, 16 | createHost, 17 | getLibraryVersion, 18 | getVersionFromPackageJson, 19 | } from '../utils'; 20 | 21 | import { Schema as NgAddOptions } from './schema'; 22 | import { workspaces } from '@angular-devkit/core'; 23 | import { 24 | WorkspaceDefinition, 25 | WorkspaceHost, 26 | } from '@angular-devkit/core/src/workspace'; 27 | 28 | function addDeployBuilderToProject( 29 | tree: Tree, 30 | host: WorkspaceHost, 31 | workspace: WorkspaceDefinition, 32 | options: NgAddOptions 33 | ) { 34 | if (!options.project) { 35 | if (workspace.extensions.defaultProject) { 36 | options.project = workspace.extensions.defaultProject as string; 37 | } else { 38 | throw new SchematicsException( 39 | 'No Angular project selected and no default project in the workspace' 40 | ); 41 | } 42 | } 43 | 44 | const project = workspace.projects.get(options.project); 45 | if (!project) { 46 | throw new SchematicsException( 47 | 'The specified Angular project is not defined in this workspace' 48 | ); 49 | } 50 | 51 | if (project.extensions.projectType !== 'application') { 52 | throw new SchematicsException( 53 | `Deploy requires an Angular project type of "application" in angular.json` 54 | ); 55 | } 56 | 57 | if (!project.targets.get('build')?.options?.outputPath) { 58 | throw new SchematicsException( 59 | `Cannot read the output path (architect.build.options.outputPath) of the Angular project "${options.project}" in angular.json` 60 | ); 61 | } 62 | 63 | project.targets.add({ 64 | name: 'deploy', 65 | builder: 'ngx-deploy-docker:deploy', 66 | options: { 67 | account: options.account, 68 | }, 69 | }); 70 | 71 | workspaces.writeWorkspace(workspace, host); 72 | return tree; 73 | } 74 | 75 | function prepareDockerFiles( 76 | tree: Tree, 77 | workspace: WorkspaceDefinition, 78 | options: NgAddOptions 79 | ): Rule { 80 | const sourceTemplates = url('./files'); 81 | 82 | const outputPath = 83 | workspace.projects.get(options.project)?.targets.get('build')?.options 84 | ?.outputPath || ''; 85 | const version = getVersionFromPackageJson(tree); 86 | const sourceParametrizedTemplates = apply(sourceTemplates, [ 87 | template({ 88 | outputPath, 89 | version, 90 | }), 91 | ]); 92 | 93 | return mergeWith(sourceParametrizedTemplates); 94 | } 95 | 96 | export const ngAdd = (options: NgAddOptions) => async ( 97 | tree: Tree, 98 | context: SchematicContext 99 | ) => { 100 | const host = createHost(tree); 101 | const { workspace } = await workspaces.readWorkspace('/', host); 102 | 103 | const version = getLibraryVersion(); 104 | addPackageToPackageJson(tree, 'ngx-deploy-docker', `^${version}`); 105 | context.logger.log( 106 | 'info', 107 | `🐳 Added "ngx-deploy-docker@^${version}" into devDependencies` 108 | ); 109 | 110 | if (options.skipInstall) { 111 | context.logger.log( 112 | 'warn', 113 | `❗️ The "--skip-install" flag was present, don't forget to install package manually` 114 | ); 115 | } else { 116 | context.logger.log('info', `📦 Installing added packages...`); 117 | context.addTask(new NodePackageInstallTask()); 118 | } 119 | 120 | addDeployBuilderToProject(tree, host, workspace, options); 121 | context.logger.log('info', `🚀 Deploy Builder added to your project`); 122 | 123 | context.logger.log('info', 'Preparing some 🐳 files...'); 124 | return chain([prepareDockerFiles(tree, workspace, options)]); 125 | }; 126 | -------------------------------------------------------------------------------- /schematics/ng-add/schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | account: string; 3 | project: string; 4 | skipInstall: boolean; 5 | } 6 | -------------------------------------------------------------------------------- /schematics/ng-add/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "NgxDeployDockerAddSchematic", 4 | "title": "Add ngx-deploy-docker to Your Project", 5 | "type": "object", 6 | "properties": { 7 | "account": { 8 | "type": "string", 9 | "description": "Your Docker account", 10 | "x-prompt": "What is your Docker account/username?", 11 | "alias": "a" 12 | }, 13 | "project": { 14 | "type": "string", 15 | "description": "The name of the project", 16 | "alias": "p" 17 | }, 18 | "skipInstall": { 19 | "type": "boolean", 20 | "description": "Skip installing after adding ngx-deploy-docker", 21 | "default": false 22 | } 23 | }, 24 | "required": [], 25 | "additionalProperties": false 26 | } 27 | -------------------------------------------------------------------------------- /schematics/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | import { virtualFs, workspaces } from '@angular-devkit/core'; 5 | import { SchematicsException, Tree } from '@angular-devkit/schematics'; 6 | 7 | export function createHost(tree: Tree): workspaces.WorkspaceHost { 8 | return { 9 | async readFile(path: string): Promise { 10 | const data = tree.read(path); 11 | if (!data) { 12 | throw new SchematicsException('File not found.'); 13 | } 14 | return virtualFs.fileBufferToString(data); 15 | }, 16 | async writeFile(path: string, data: string): Promise { 17 | return tree.overwrite(path, data); 18 | }, 19 | async isDirectory(path: string): Promise { 20 | return !tree.exists(path) && tree.getDir(path).subfiles.length > 0; 21 | }, 22 | async isFile(path: string): Promise { 23 | return tree.exists(path); 24 | }, 25 | }; 26 | } 27 | 28 | export function getLibraryVersion() { 29 | return JSON.parse( 30 | fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8') 31 | ).version; 32 | } 33 | 34 | export function getVersionFromPackageJson(host: Tree): string { 35 | const sourceText = host.read('package.json')!.toString('utf-8'); 36 | const packageJson = JSON.parse(sourceText); 37 | 38 | return packageJson.version || ''; 39 | } 40 | 41 | export function addPackageToPackageJson( 42 | tree: Tree, 43 | pkg: string, 44 | version: string 45 | ): Tree { 46 | if (tree.exists('package.json')) { 47 | const sourceText = tree.read('package.json')!.toString('utf-8'); 48 | const json = JSON.parse(sourceText); 49 | 50 | if (!json.devDependencies) { 51 | json.devDependencies = {}; 52 | } 53 | 54 | if (!json.devDependencies[pkg]) { 55 | json.devDependencies[pkg] = version; 56 | json.devDependencies = sortObjectByKeys(json.devDependencies); 57 | } 58 | 59 | tree.overwrite('package.json', JSON.stringify(json, null, 2)); 60 | } 61 | 62 | return tree; 63 | } 64 | 65 | function sortObjectByKeys(obj: any) { 66 | return Object.keys(obj) 67 | .sort() 68 | .reduce((result: any, key) => { 69 | result[key] = obj[key]; 70 | return result; 71 | }, {}); 72 | } 73 | -------------------------------------------------------------------------------- /src/builders.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@angular-devkit/architect/src/builders-schema.json", 3 | "builders": { 4 | "deploy": { 5 | "implementation": "./deploy/builder", 6 | "schema": "./deploy/schema.json", 7 | "description": "Deploy builder" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/deploy/actions.ts: -------------------------------------------------------------------------------- 1 | import { BuildTarget } from './../interfaces'; 2 | import { 3 | BuilderContext, 4 | targetFromTargetString, 5 | } from '@angular-devkit/architect'; 6 | import { json, logging } from '@angular-devkit/core'; 7 | 8 | import { Schema } from './schema'; 9 | 10 | export default async function deploy( 11 | engine: { 12 | run: (options: Schema, logger: logging.LoggerApi) => Promise; 13 | }, 14 | context: BuilderContext, 15 | buildTarget: BuildTarget, 16 | options: Schema 17 | ) { 18 | // 1. BUILD 19 | if (options.noBuild) { 20 | context.logger.info(`📦 Skipping build`); 21 | } else { 22 | if (!context.target) { 23 | throw new Error('Cannot execute the build target'); 24 | } 25 | 26 | const overrides = { 27 | ...(options.baseHref && { baseHref: options.baseHref }), 28 | }; 29 | 30 | context.logger.info(`📦 Building "${context.target.project}"`); 31 | context.logger.info(`📦 Build target "${buildTarget.name}"`); 32 | 33 | const build = await context.scheduleTarget( 34 | targetFromTargetString(buildTarget.name), 35 | { 36 | ...buildTarget.options, 37 | ...overrides, 38 | } 39 | ); 40 | const buildResult = await build.result; 41 | 42 | if (!buildResult.success) { 43 | throw new Error('Error while building the app.'); 44 | } 45 | } 46 | 47 | // 2. DEPLOYMENT 48 | 49 | if (!options.imageName) { 50 | options.imageName = await context.target?.project; 51 | } 52 | 53 | await engine.run(options, (context.logger as unknown) as logging.LoggerApi); 54 | } 55 | -------------------------------------------------------------------------------- /src/deploy/builder.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BuilderContext, 3 | BuilderOutput, 4 | createBuilder, 5 | } from '@angular-devkit/architect'; 6 | import { experimental, normalize } from '@angular-devkit/core'; 7 | import { NodeJsSyncHost } from '@angular-devkit/core/node'; 8 | 9 | import * as engine from '../engine/engine'; 10 | import deploy from './actions'; 11 | import { Schema } from './schema'; 12 | 13 | // Call the createBuilder() function to create a builder. This mirrors 14 | // createJobHandler() but add typings specific to Architect Builders. 15 | export default createBuilder( 16 | async (options: Schema, context: BuilderContext): Promise => { 17 | if (!context.target) { 18 | throw new Error('Cannot deploy the application without a target'); 19 | } 20 | 21 | const buildTarget = { 22 | name: options.buildTarget || `${context.target.project}:build:production`, 23 | }; 24 | 25 | try { 26 | await deploy(engine, context, buildTarget, options); 27 | } catch (e) { 28 | context.logger.error('❌ An error occurred when trying to deploy:'); 29 | context.logger.error(e.message); 30 | return { success: false }; 31 | } 32 | 33 | return { success: true }; 34 | } 35 | ); 36 | -------------------------------------------------------------------------------- /src/deploy/schema.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Deployment of Angular CLI applications to the file system 3 | */ 4 | export interface Schema { 5 | /** 6 | * Base url for the application being built. Same as `ng build --base-href=/XXX/`. 7 | */ 8 | baseHref?: string; 9 | /** 10 | * A named build target, as specified in the `configurations` section of angular.json. Each named target is accompanied by a configuration of option defaults for that target. Same as `ng build --configuration=XXX`. 11 | */ 12 | buildTarget?: string; 13 | /** 14 | * Skip build process during deployment. 15 | */ 16 | noBuild?: boolean; 17 | 18 | /** 19 | * The Name of the Docker Image you want to deploy. If not present, the project name will be taken. 20 | */ 21 | imageName?: string; 22 | 23 | /** 24 | * The Account you want to publish the image too. Default is your docker username. 25 | */ 26 | account?: string; 27 | 28 | /** 29 | * The Tag you want to publish the image with. 30 | */ 31 | tag?: string; 32 | } 33 | -------------------------------------------------------------------------------- /src/deploy/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "Schema", 3 | "title": "schema", 4 | "description": "Deployment of Angular CLI applications to the file system", 5 | "properties": { 6 | "baseHref": { 7 | "type": "string", 8 | "description": "This is an example how to override the workspace set of options. --- Base url for the application being built. Same as `ng build --base-href=/XXX/`." 9 | }, 10 | "buildTarget": { 11 | "type": "string", 12 | "description": "A named build target, as specified in the `configurations` section of angular.json. Each named target is accompanied by a configuration of option defaults for that target. Same as `ng run :." 13 | }, 14 | "noBuild": { 15 | "type": "boolean", 16 | "default": false, 17 | "description": "Skip build process during deployment." 18 | }, 19 | "imageName": { 20 | "type": "string", 21 | "description": "The Name of the Docker Image you want to deploy. If not present, the project name will be taken." 22 | }, 23 | "account": { 24 | "type": "string", 25 | "default": "", 26 | "description": "The Account you want to publish the image too. Default is your docker username.", 27 | "alias": "a" 28 | }, 29 | "tag": { 30 | "type": "string", 31 | "description": "The Tag you want to publish the image with.", 32 | "default": "latest", 33 | "alias": "t" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/engine/engine.ts: -------------------------------------------------------------------------------- 1 | import { logging } from '@angular-devkit/core'; 2 | 3 | import { Schema } from '../deploy/schema'; 4 | 5 | import * as child_process from 'child_process'; 6 | 7 | function getImageNameWithTag(options: Schema): string { 8 | return options.account !== '' 9 | ? `${options.account}/${options.imageName}:${options.tag}` 10 | : `${options.imageName}:${options.tag}`; 11 | } 12 | 13 | async function buildDockerImage( 14 | imageNameWithTag: string, 15 | logger: logging.LoggerApi 16 | ): Promise { 17 | // context.reportStatus(`Executing "docker build"...`); 18 | const child = child_process.spawn( 19 | 'docker', 20 | ['build', '-t', imageNameWithTag, '.'], 21 | { 22 | stdio: 'pipe', 23 | } 24 | ); 25 | 26 | child.stdout.on('data', (data) => { 27 | logger.info(data.toString()); 28 | }); 29 | child.stderr.on('data', (data) => { 30 | logger.error(data.toString()); 31 | }); 32 | 33 | return new Promise((resolve) => { 34 | // context.reportStatus(`Done.`); 35 | child.on('close', (code) => { 36 | resolve(); 37 | }); 38 | }); 39 | } 40 | 41 | async function publishDockerImage( 42 | imageNameWithTag: string, 43 | logger: logging.LoggerApi 44 | ): Promise { 45 | const child = child_process.spawn('docker', ['push', imageNameWithTag], { 46 | stdio: 'pipe', 47 | }); 48 | 49 | child.stdout.on('data', (data) => { 50 | logger.info(data.toString()); 51 | }); 52 | child.stderr.on('data', (data) => { 53 | logger.error(data.toString()); 54 | }); 55 | 56 | return new Promise((resolve) => { 57 | // context.reportStatus(`Done.`); 58 | child.on('close', (code) => { 59 | resolve(); 60 | }); 61 | }); 62 | } 63 | 64 | export async function run(options: Schema, logger: logging.LoggerApi) { 65 | try { 66 | const imageNameWithTag = getImageNameWithTag(options); 67 | 68 | logger.info('🚧 Executing Docker Build...'); 69 | await buildDockerImage(imageNameWithTag, logger); 70 | logger.info('✔️ Docker Build was successfully'); 71 | 72 | logger.info('🚀 Publishing image to registry'); 73 | await publishDockerImage(imageNameWithTag, logger); 74 | logger.info('🎊 Successfully published image. Have a nice day.'); 75 | } catch (error) { 76 | logger.error('❌ An error occurred!'); 77 | throw error; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface WorkspaceProject { 2 | projectType?: string; 3 | architect?: Record< 4 | string, 5 | { builder: string; options?: Record } 6 | >; 7 | } 8 | 9 | export interface Workspace { 10 | defaultProject?: string; 11 | projects: Record; 12 | } 13 | 14 | export interface BuildTarget { 15 | name: string; 16 | options?: { [name: string]: any }; 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "module": "CommonJS", 7 | "target": "es2015", 8 | "noImplicitAny": false, 9 | "outDir": "dist", 10 | "rootDir": ".", 11 | "sourceMap": true, 12 | "inlineSources": true, 13 | "declaration": false, 14 | "removeComments": true, 15 | "strictNullChecks": true, 16 | "lib": [ 17 | "es2015", 18 | "dom", 19 | "es2015.promise", 20 | "es2015.collection", 21 | "es2015.iterable" 22 | ], 23 | "skipLibCheck": true, 24 | "moduleResolution": "Node", 25 | "esModuleInterop": true, 26 | "types": ["node"] 27 | }, 28 | "files": ["index.ts"], 29 | "exclude": ["./**/*.spec.ts", "./schematics/**/files/**/*"], 30 | "angularCompilerOptions": { 31 | "skipTemplateCodegen": true, 32 | "strictMetadataEmit": true, 33 | "enableSummariesForJit": false 34 | } 35 | } 36 | --------------------------------------------------------------------------------