Use github action to deploy vue app to Netlify

  • Generate a personal access token and create ACCESS_TOKEN variable under Settings -> Secrets.
  • Keep the personal access token in a safe place and do not lose it
  • Go to Actions tab of the github repo
  • Create netlify.yml under .github/workflow
  • Paste the follow code in the yaml file
# .github/workflows/netlify.yml
name: Build and Deploy to Netlify
on:
  push:
  pull_request:
    types: [opened, synchronize]
jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      # ( Build to ./dist or other directory... )
      - name: Clean install dependencies
        run: npm ci

      - name: Build app
        run: npm run build-netlify

      - name: Deploy to Netlify
        uses: nwtgck/actions-netlify@v1.1
        with:
          publish-dir: './dist'
          production-branch: master
          github-token: ${{ secrets.ACCESS_SECRET }}
          deploy-message: "Deploy from GitHub Actions"
          enable-pull-request-comment: false
          enable-commit-comment: true
          overwrites-pull-request-comment: true
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
        timeout-minutes: 1

I deploy the Tic Tac Toe game to both github page and Netlify; therefore, I use NODE_ENV variable to configure the public path in vue.config.js.

// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "/vue-tic-tac-toe/" : "/",
  outputDir: "dist",
  lintOnSave: process.env.NODE_ENV !== "production",
  devServer: {
    overlay: {
      warnings: true,
      errors: true
    }
  }
};

The NODE_ENV variable of github page is “production” by default and the full URL becomes https://<username>.github.io/vue-tic-tac-toe/. For Netlify deployment, I set the NODE_ENV variable to “netlify” and the game is hosted on https://<netlify app>/ .

Open package.json and add a task that builds the project to dist directory and publishes it to Netlify.

{
  ...
  "scripts": {
    ...
    "build-netlify": "NODE_ENV=netlify vue-cli-service build"
  },
  ...
}

Login to Netlify to look up NETIFY_SITE_ID and NETIFY_AUTH_TOKEN.

Go to Team > Site > Settings > Site Information > API ID to copy down the NETLIFY_SITE_ID

Go to User Settings > Applications > Personal access token and clicks New Access Token button to generate a token. The token is your NETLIFY_AUTH_TOKEN and it should be kept in a safe location.

Now, you are ready to create NETLIFY_SITE_ID and NETLIFY_AUTH_TOKEN variables under Settings -> Secrets in Github.

We are done! There is no need to write custom deployment script and travis configuration file to automate the CI/CD process. Github notifies Netlify to automate the build process and publish the site when latest changes are committed.

References:

Use github action to deploy vue app to github page

  • Generate a personal access token and create ACCESS_TOKEN variable under Settings -> Secrets.
  • Keep the personal access token in a safe place and do not lose it
  • Go to Actions tab of the github repo
  • Create main.yml under .github/workflow
 name: Deploy to github pages
 on:
   push:
    branches:
     - master
 jobs:
    gh-pages-deploy:
      name: Deploying to gh-pages
      runs-on: ubuntu-latest
      steps:
        - name: Setup Node.js for use with actions
          uses: actions/setup-node@v1.1.0
          with:
            version:  12.x
        - name: Checkout branch
          uses: actions/checkout@v2

        - name: Clean install dependencies
          run: npm ci

        - name: Build app
          run: npm run build
        
        - name: deploy
          uses: peaceiris/actions-gh-pages@v3
          with:
           github_token: ${{ secrets.ACCESS_TOKEN }}
           publish_dir: ./dist

There is no need to write custom deployment script and travis configuration file to automate the CI/CD process. Github does it for you when latest changes are pushed to master to start the build process and copy the contents of output folder to gh-pages branch.

References:

Use github action to deploy angular app to github page

  • Generate a personal access token and create ACCESS_TOKEN variable in Settings -> Secrets.
  • Keep the personal access token in a safe place and do not lose it
  • Go to Actions tab of the github repo
  • Create main.yml under .github/workflow
name: CI
on:
  push:
    branches:
      - master
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          persist-credentials: false

      - name: Install
        run: |
          npm install
          npm run github-page-deploy
      - name: Build and Deploy Repo
        uses: JamesIves/github-pages-deploy-action@releases/v3
        with:
          ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
          BASE_BRANCH: master
          BRANCH: gh-pages
          CLEAN: true
          GIT_CONFIG_NAME: <author name>
          GIT_CONFIG_EMAIL: <email address>
          FOLDER: dist/<repo name>

References:

Deploy angular app to Github Page by Travis CI

1) Install npm dependencies

angular-cli-ghpages is a plugin that pushes angular application to gh-pages branch.

npm install angular-cli-ghpages --save-dev 

Add prettier schematic to Angular project

npm install -g @schuchard/prettier
ng g @schuchard/prettier:add

2) Create a .travis.yml file that details the deployment steps

3) Specify language is node_js and node version is 8

language: node_js
node_js:
    - '8'

4) Cache npm dependencies and production build directory, dist.

cache:
    npm: true
    directories:
        - node_modules
        - dist/

5) Create environment variables to store github organization, repo name, github username and github email respectively. GH_TOKEN is your secret github token that is saved under Settings of your repository. You can find the link to generate github token here

env:
    global:
        - GITHUB_ORG="https://GH_TOKEN@github.com"
        - REPO_NAME="<repository name>"
        - GITHUB_NAME="<username>"
        - GITHUB_EMAIL="<email address>"

6) Add “before_script” to install npm dependencies without modifying package-lock.json file

before_script:
    - npm install --no-save

7) Add “script” to run different tasks before the final deployment

script:
    - npm audit
    - npm run prettier
    - npm run lint
    - npm run test-headless
    - npm run build-ngh

branches:
    only:
        - master

8) The above tasks are created in scripts of package.json:

  • npm audit audits npm dependencies to ensure vulnerabilities do not exist
  • npm run prettier formats files with Prettier code formatter
  • npm run lint performs linting on source files to identify any lint problem and aborts the process prematurely
  • npm run test-headless executes test cases in headless Chrome browser and terminates if any test case fails
  • npm run build-ngh compiles the source codes on master branch to production build and places the artifacts to dist/<project> directory
"scripts": {
    "prettier": "prettier --write \"**/*.{js,json,scss,ts}\"",
    "lint": "ng lint",
    "build-ngh": "ng build --prod --base-href \"https://railsstudent.github.io/<repository name>/\"",
    "test-headless": "ng test --watch=false --browsers=ChromeHeadless"
  }

9) If all script tasks finishes successfully, the task in “after_success” is executed to push the static pages to gh_pages branch

after_success:
    - ngh --repo="$GITHUB_ORG/$GITHUB_NAME/$REPO_NAME.git" --name="$GITHUB_NAME" --email="$GITHUB_EMAIL" --dir=dist/ng-rxjs-state-management-demo --no-silent

10) Add notifications configuration to receive notification email when travis build fails

notifications:
email:
recipients:
- somebody@example.come
on_success: never # default: change
on_failure: always # default: always

11) Navigate to https://<username>.github.io/<repo name>/ to view the static pages

2019 iT 邦幫忙鐵人賽 – Angular 大師之路

Stackbliz demos

[Angular 大師之路] Day 17 – 使用 HTTP_INTERCEPTORS 攔截 Http Request

Demo of Day 17

[Angular 大師之路] Day 18 – 使用 ErrorHandler 集中處理錯誤

Demo of Day 18

[Angular 大師之路] Day 19 – 使用 APP_INITIALIZER 在程式運行早期處理資料

Demo of Day 19

[Angular 大師之路] Day 20 – 在 @NgModule 的 providers: [] 自由更換注入內容 (1)

Demo of Day 20

[Angular 大師之路] Day 21 – 在 @NgModule 的 providers: [] 自由更換注入內容 (2)

Demo of Day 21

[Angular 大師之路] Day 22 – 各種在程式中取的注入 token 實體的方法

Demo of Day 22

[Angular 大師之路] Day 23 – 認識 InjectionToken

Demo of Day 23

[Angular 大師之路] Day 27 – 開場閒聊

Demo of Day 27

[Angular 大師之路] Day 29 – 在 Angular 中應用 RxJS 的 operators (1) – 基礎篇

Demo of Day 29

[Angular 大師之路] Day 30 – 在 Angular 中應用 RxJS 的 operators (2) – 進階應用

Demo of Day 30

Add code coverage in Jest in Vue project

Github: https://github.com/railsstudent/vue-2048

1) Assume Vue project is created by vue-cli 3 and unit testing is enabled

2) Add code coverage in jest configuration in package.json

 "jest": {
    "moduleFileExtensions": [
      "js",
      "jsx",
      "json",
      "vue"
    ]
    ...  // Delete the rest for brevity
 }

3) Append the following configurations in jest configuration

"collectCoverage": true,
"collectCoverageFrom": [
  "src/**/*.vue",
  "!src/App.vue"
 ],
 "coverageDirectory": "coverage/",
 "coverageReporters": [
   "lcov"
 ]

Set collectionCoverage to true to enable code coverage.
coverageDirectory indicates code coverage outputs are generated in coverage/ directory.

"collectCoverageFrom": [
  "src/**/*.vue",
  "!src/App.vue"
 ]

Collect code coverage data from all Vue components except src/App.vue

"coverageReporters": [
   "lcov"
 ]

Use lcov code coverage reporter. Default value is [“json”, “lcov”, “text”] array and any istanbul reporter is accepted.

4) Run all unit tests and gather code coverage data automatically.

npm run test:unit

5) Install http-server and use local development server to serve code coverage files in browser

with npm
npm install http-server --save-dev o
 
with yarn 
yarn add http-server -D

6) Add npm script command to start http server with base directory coverage/

"scripts": {
 "serve:coverage": "http-server coverage/lcov-report -p 8888",
}

Type the following command in the terminal

yarn serve:coverage

and navigate to http://localhost:8888/

Reference:
https://jestjs.io/docs/en/configuration#collectcoverage-boolean

Use Karma, Mocha, Chai and Coverage to run headless unit tests and generate lcov code coverage

Github: https://github.com/railsstudent/image-gallery-native-js

1) Install gulp, mocha, chai, puppeteer, http-server as dev-dependencies

yarn add gulp mocha chai puppeteer http-server -D

2) Install all karma dependencies as dev-dependencies.

yarn add karma karma-chai karma-mocha karma-chrome-launcher mocha chai -D

3) Create test/bootstrap.karma.js file to share global variables among unit test cases.

'use strict';
 
const expect = chai.expect;
const assert = chai.assert;

4) Run karma command to generate karma.conf.js to specify Karma configuration.

./node_modules/.bin/karma init
// Karma configuration
// Generated on Sun Aug 26 2018 09:31:46 GMT+0800 (HKT)
 
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function(config) {
  config.set({
 
    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',
 
    plugins: [
      "karma-chai",
      "karma-chrome-launcher",
      "karma-mocha",
      "karma-coverage"
    ],
 
    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai'],
 
    // list of files / patterns to load in the browser
    files: [
      'src/**/*.js',
      'test/bootstrap.karma.js',
      'test/**.test.js'
    ],
 
    // list of files / patterns to exclude
    exclude: [
      'test/gallery.test.js'
    ],
 
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'src/**/!(gallery).js': 'coverage'
    },
 
    // optionally, configure the reporter
    coverageReporter: {
      type : 'lcov',
      dir : 'coverage/',
      subdir: '.'
    },
 
    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage'],
 
    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,
 
    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['ChromeHeadless'],
 
    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,
  })
}

Explicitly list karma plugins for chai, browser launcher, mocha and code coverage.

plugins: [
   "karma-chai",
   "karma-chrome-launcher",
   "karma-mocha",
  "karma-coverage"
 ]

Include mocha and chai required by the unit test cases.

// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai'],

Load src/, test/bootstrap.karma.js and test/**/*.test.js in browser. Exclude test/gallery.test.js since this test file contains automated UI test cases

// list of files / patterns to load in the browser
files: [
  'src/**/*.js',
  'test/bootstrap.karma.js',
  'test/**.test.js'
],
 
// list of files / patterns to exclude
exclude: [
  'test/gallery.test.js'
]

Use Chromium instance of puppeteer to execute headless browser test

process.env.CHROME_BIN = require('puppeteer').executablePath();
browsers: ['ChromeHeadless'],

Add lcov code coverage. Code coverage results are kept in coverage/lcov-report directory

preprocessors: {
 'src/**/!(gallery).js': 'coverage'
},
 
// optionally, configure the reporter
coverageReporter: {
   type : 'lcov',
   dir : 'coverage/',
   subdir: '.'
}

Add reporters for headless browser testing and code coverage

reporters: ['progress', 'coverage']

5) Install gulp-karma to integrate karma with gulp

yarn add gulp-karma -D

Add gulp test task to run karma test runner

/**
 * Run test once and exit
 */
gulp.task('test', function (done) {
    new KarmaServer({
        configFile: __dirname + '/karma.conf.js',
        singleRun: true
    }, done).start();
});

6) Set up npm script commands in package.json.

"scripts": {
   "test": "gulp test",
   "serve:coverage": "gulp test && http-server coverage/lcov-report/ -p 8001"
}

Test unit test cases in headless browser

yarn test

Serve lcov code coverage results at http://localhost:8001

yarn serve:coverage

7) Write mocha test case in test file.

'use strict';
 
describe('slideshow test', function() {
  let first, middle;
  let imageUrls = [];
  let slideshow = null;
  before(function() {
    first = 0;
    middle = 1;
  });
 
  describe('single image slideshow', () => {
    before(function() {
      // runs before all tests in this block
      imageUrls = [
        'image1.jpg'
      ];
      slideshow = new SlideShow(imageUrls);
      slideshow.setCurrentIndex(first);
    });
 
    it('slideshow has 1 image', function() {
      assert.strictEqual(slideshow.totalCount(), imageUrls.length);
    });
 
    it('current url should be the first url', function() {
      assert.strictEqual(slideshow.currentUrl(), 'image1.jpg');
    });
 
    it('initial index is always 0', function() {
      assert.strictEqual(slideshow.currentIndex(), first);
    });
 
    it('Slide show with 1 image is always the first image', function() {
      assert.strictEqual(slideshow.isFirstImage(), true);
    });
 
    it('Slide show with 1 image is always last image', function() {
      assert.strictEqual(slideshow.isLastImage(), true);
    });
 
    it('Show next image does nothing when there is 1 image', function() {
      assert.strictEqual(slideshow.showNext(), false);
      assert.strictEqual(slideshow.currentIndex(), first);
    });
 
    it('Slide prev image with 1 image is always last image', function() {
      assert.strictEqual(slideshow.showPrev(), false);
      assert.strictEqual(slideshow.currentIndex(), first);
    });
  });
});

Finally, we are done.