├── ISSUE_TEMPLATE.md ├── CONTRIBUTING.md ├── keys.enc ├── public ├── favicon.ico ├── manifest.json └── index.html ├── src ├── App.test.js ├── index.js ├── index.css ├── registerServiceWorker.js └── App.js ├── .gitignore ├── .travis.yml ├── package.json ├── LICENSE ├── CODE_OF_CONDUCT.md ├── README.md └── deploy.sh /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | I have an issue with: 2 | * 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Literally just fork and PR and we can talk. 2 | -------------------------------------------------------------------------------- /keys.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TejasQ/basically-continuous-deployment/HEAD/keys.enc -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TejasQ/basically-continuous-deployment/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file tells Travis what's up 2 | language: node_js 3 | node_js: 4 | - stable 5 | 6 | # When Travis gets notified of a new build, do this 7 | script: bash ./deploy.sh 8 | 9 | # Here's some variables for Travis 10 | env: 11 | global: 12 | - ENCRYPTION_LABEL: "d0e50c888661" # This is important, it's documented in deploy.sh 13 | - COMMIT_AUTHOR_EMAIL: "tejas@tejas.qa" 14 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basically-continuous-deployment", 3 | "version": "1.0.0", 4 | "author": "Tejas Kumar ", 5 | "homepage": "http://tejasq.github.io/basically-continuous-deployment", 6 | "dependencies": { 7 | "react": "^16.0.0", 8 | "react-dom": "^16.0.0", 9 | "react-scripts": "1.0.14" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test --env=jsdom", 15 | "eject": "react-scripts eject" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tejas Kumar 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 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | Gotta have the pretty, y'all. 💄 3 | */ 4 | 5 | :root { 6 | --spacing: 16px; 7 | --colors__main: #a63d40; 8 | --colors__secondary: #0c6291; 9 | --colors__disabled: #666; 10 | --breakpoint__tablet: 768px; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | } 18 | 19 | html, 20 | body { 21 | margin: 0; 22 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", 23 | "Segoe UI Emoji", "Segoe UI Symbol"; 24 | font-size: 13px; 25 | } 26 | 27 | a:link, 28 | a:visited { 29 | transition: 0.15s ease color; 30 | color: var(--colors__main); 31 | } 32 | 33 | a:hover { 34 | color: var(--colors__secondary); 35 | } 36 | 37 | @media screen and (min-width: var(--breakpoint__tablet)) { 38 | html, 39 | body { 40 | margin: var(--spacing) 0; 41 | } 42 | } 43 | 44 | img { 45 | max-width: 100%; 46 | margin-bottom: var(--spacing); 47 | } 48 | 49 | header, 50 | main { 51 | margin: 0 auto; 52 | max-width: 740px; 53 | } 54 | 55 | main { 56 | padding: 20px; 57 | } 58 | 59 | header { 60 | padding: var(--spacing); 61 | } 62 | 63 | h1 { 64 | margin: 0; 65 | font-size: 1.8rem; 66 | } 67 | 68 | ul { 69 | margin: calc(var(--spacing)) 0; 70 | padding-left: calc(var(--spacing) * 2); 71 | } 72 | 73 | main > ul { 74 | margin: calc(var(--spacing) * 2) 0; 75 | padding: 0; 76 | } 77 | 78 | li + li { 79 | margin-top: var(--spacing); 80 | } 81 | 82 | @media screen and (min-width: var(--breakpoint__tablet)) { 83 | h1 { 84 | font-size: 2rem; 85 | } 86 | } 87 | 88 | p { 89 | margin: var(--spacing) auto; 90 | } 91 | 92 | table { 93 | width: 100%; 94 | border-collapse: collapse; 95 | } 96 | 97 | .table__left { 98 | width: 150px; 99 | } 100 | 101 | td { 102 | padding: calc(var(--spacing)/4); 103 | font-size: 1rem; 104 | } 105 | 106 | thead { 107 | font-size: 1rem; 108 | font-weight: bold; 109 | } 110 | 111 | button { 112 | width: 100%; 113 | padding: calc(var(--spacing)/2); 114 | border: 0; 115 | font-size: 1rem; 116 | transition: 0.15s ease background-color; 117 | cursor: pointer; 118 | background-color: var(--colors__main); 119 | color: white; 120 | -webkit-appearance: none; 121 | } 122 | 123 | button:hover { 124 | background-color: var(--colors__secondary); 125 | } 126 | 127 | button:disabled { 128 | background-color: var(--colors__disabled); 129 | } 130 | 131 | input { 132 | width: 100%; 133 | font: inherit; 134 | } 135 | 136 | code { 137 | margin: calc(var(--spacing)/4) 0; 138 | padding: calc(var(--spacing)/4); 139 | display: inline-block; 140 | color: white; 141 | background: black; 142 | --webkit-font-smoothing: subpixel-antialiased; 143 | } 144 | 145 | code a:link, 146 | code a:visited { 147 | color: white; 148 | } 149 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 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. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at tejas@tejas.qa. The project team will review and investigate all complaints, and will respond in a way that it deems 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _Basically_, [Continuous Deployment](https://www.thoughtworks.com/continuous-integration) 2 | ![Build Status](https://travis-ci.org/TejasQ/basically-continuous-deployment.svg?branch=master) 3 | 4 | This project was created to help introduce the concept of _Continuous Deployment_ to anyone interested. 5 | 6 | _Basically_ how it works is: 7 | 8 | * You `git push` to a GitHub (or something else) server. 9 | * The `git` server uses a WebHook to connect with a CI/CD system. 10 | * The CI/CD thing builds your project, and deploys it somewhere. 11 | 12 | Simple, right? This webpage actually, was built with a Continuous Deployment script! 13 | 14 | [Check out the source code](https://github.com/TejasQ/basically-continuous-deployment/blob/master/deploy.sh) to understand how it works. 15 | 16 | ## How does _this_ project work? 17 | 18 | Glad you asked! 19 | 20 | * It was created with [`create-react-app`](https://github.com/facebookincubator/create-react-app). 21 | * It uses [Travis CI](https://travis-ci.org/) as the CI/CD thing, which starts a new build on `git push`. 22 | * When Travis starts a new build, it executes [this script](https://github.com/TejasQ/basically-continuous-deployment/blob/master/deploy.sh). 23 | * It is instructed to use the script, with [_this_ script.](https://github.com/TejasQ/basically-continuous-deployment/blob/master/.travis.yml) 24 | * I highly recommend reading the deploy script above. It is heavily documented for you in true _Basically_ fashion, in order to help you understand what's actually happening. 25 | * After Travis is done, everything's deployed on [`gh-pages`](https://github.com/TejasQ/basically-continuous-deployment/tree/gh-pages), and things are live. 26 | 27 | ## What's this about keys? 🗝 28 | 29 | Basically, Travis is going to be pushing to your GitHub account. Travis needs to be able to say HEY IM TEJAS in order to use Tejas' GitHub: it needs my key; my SSH key. 30 | 31 | And so, I've got to: 32 | 33 | * Generate some keys: 34 | * In a terminal, type: 35 | `ssh-keygen -t rsa -b 4096 -C "hello@tej.as" # YOUR EMAIL HERE` 36 | * It'll ask you where to save it. Save the key somewhere familiar. 37 | * It'll ask you for a passphrase. I usually leave this blank. 38 | * It'll generate 2 files for you: 39 | * One ending with .pub **(make a note of this)**. 40 | * And one with the name you gave it. 41 | * Encrypt them with Travis: 42 | * Make sure you have the [Travis CLI](https://github.com/travis-ci/travis.rb) installed. 43 | * In the terminal, run: 44 | `travis encrypt-file WHATEVER_YOU_NAMED_YOUR_KEY` 45 | * It will then create a `.enc` file based your key's filename. 46 | * It'll also say something back to you like: 47 | `openssl aes-256-cbc -K $encrypted_0a6446eb3ae3_key -iv $encrypted_0a6446eb3ae3_key -blah blah` 48 | * Copy the portion where we have `0a6446eb3ae3` written above. You'll need it. 49 | * Add them (the _encrypted ones!_) to my project: 50 | * At this point, you have add your .enc file to your git repo and commit it. 51 | * You can throw away your key at this point. 52 | * Tell Travis how to decrypt them: 53 | * In your [.travis.yml](https://github.com/TejasQ/basically-continuous-deployment/blob/master/.travis.yml), you'll want to add an `ENCRYPTION_LABEL` with that red thing you copied above. 54 | * See [this project's .travis.yml](https://github.com/TejasQ/basically-continuous-deployment/blob/master/.travis.yml) as an example. 55 | * Add the keys to my GitHub account: 56 | * The last step is actually adding the public part of your key to your GitHub profile to say "yes, the Travis thing using my key is basically me". 57 | * [Go here](https://github.com/settings/keys), click the green **New SSH Key**, and paste the contents of your .pub file in the `key` field, giving it an appropriate title. 58 | * Bam! 59 | 60 | 61 | Whew! Now, Travis can properly push your shiny new `gh-pages` to your GitHub project. 62 | 63 | ## Thanks to 64 | - [ThoughtWorks](https://www.thoughtworks.com/continuous-integration) for an excellent article on the subject. 65 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # The above thing is needed for bash 3 | set -e # Exit with nonzero exit code if anything fails 4 | 5 | # Let's tell Travis we're in production. 6 | export NODE_ENV='production' 7 | 8 | # So if master is pushed, update gh-pages. 9 | SOURCE_BRANCH="master" 10 | TARGET_BRANCH="gh-pages" 11 | 12 | # A simple alias for our build command. 13 | function doCompile { 14 | npm run build 15 | } 16 | 17 | # If someone just opened a pull request, or pushed some other branch, 18 | if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then 19 | 20 | # Just build and exit. 21 | doCompile 22 | exit 0 23 | fi 24 | 25 | # If not, store some variables. 26 | REPO=`git config remote.origin.url` 27 | REPO_LINK=${REPO/https:\/\/github.com\//git@github.com:} 28 | COMMIT_HASH=`git rev-parse --verify HEAD` 29 | 30 | # Clone this repo into a folder (on Travis) called "clone" 31 | git clone $REPO clone 32 | 33 | # Go into this folder 34 | cd clone 35 | 36 | # If we fail to check out the target branch as a new branch 37 | # Or, if the target branch (gh-pages) already exists, 38 | if ! git checkout -b $TARGET_BRANCH $SOURCE_BRANCH; then 39 | 40 | # Delete it 41 | git branch -D $TARGET_BRANCH 42 | 43 | # Create a copy of the source branch (master) named target branch (gh-pages) 44 | git checkout -b $TARGET_BRANCH $SOURCE_BRANCH 45 | 46 | # End conditional 47 | fi 48 | 49 | # Copy over all the node modules so we don't have to `yarn install` again. 50 | cp -a ../node_modules node_modules 51 | 52 | # Run our compile script, inside the built folder. 53 | # This is expected to create a `build` folder with production, compiled code. 54 | # If your build script does something else, please account for in this file. 55 | doCompile 56 | 57 | # Find all files that are not: 58 | # - our deployment keys (keys.enc) 59 | # - our final compiled code (build) 60 | # - the .git folder (LOL) 61 | # - the current folder (. – also LOL) 62 | # - don't be recursive 63 | # 64 | # and then for each found file, 65 | # delete it. 66 | find . -maxdepth 1 ! -name keys.enc ! -name build ! -name .git ! -name . -exec rm -rf {} + 67 | 68 | # Get the current directory. 69 | cwd=`pwd` 70 | 71 | # Copy the contents of the build folder into this current directory. 72 | cp -a "${cwd}/build"/. "${cwd}" 73 | 74 | # Now since the `build` stuff is in the root, delete build. 75 | rm -rf build 76 | 77 | # Close the index.html to 404.html 78 | # On gh-pages, this allows you to use react-router and other JS routers for your site. 79 | cp index.html 404.html # react-router woooo 80 | 81 | # You can configure this git instance to have its own name and email! 82 | git config user.name "Travis Kumar" 83 | git config user.email "$COMMIT_AUTHOR_EMAIL" 84 | 85 | # If nothing changed, 86 | if git diff --quiet; then 87 | echo "Nothing changed." 88 | 89 | # die. 90 | exit 0 91 | fi 92 | 93 | # Otherwise, add all files. 94 | git add -A . 95 | 96 | # Commit them with a message including the hash. 97 | git commit -m "Deploy to GitHub Pages: ${COMMIT_HASH}" 98 | 99 | # Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc 100 | # THIS STEP IS KEY! How do I get keys?! 101 | # https://tejasq.github.io/basically-continuous-deployment/index.html#keys 102 | 103 | # Store some variables 104 | ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" 105 | ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" 106 | ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} 107 | ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} 108 | 109 | # Use the key: 110 | # openssl [type of encryption] -K [key] -iv [initialization vector] 111 | # An initialization vector is basically a random number that guarantees the encrypted text is unique, that is used 112 | # in encrypting AND decrypting something. 113 | openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in keys.enc -out key -d 114 | 115 | # Update permissions 116 | chmod 600 key 117 | 118 | # Add key 119 | eval `ssh-agent -s` 120 | ssh-add key 121 | 122 | # Push the new gh-pages with the -f tag, overwriting it completely. 123 | # Basically, this guarantees that `gh-pages` is ALWAYS 1 commit ahead of master, with the latest production code. 124 | git push -f -u $REPO_LINK $TARGET_BRANCH 125 | exit 0 126 | 127 | # Thank you for reading. Happy internetting! 🚀 128 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | class App extends Component { 4 | render() { 5 | return ( 6 |
7 |

Basically, Continuous Deployment

8 | Build Status 9 |
10 |
11 |

12 | This project was created to help introduce the concept of Continuous Deployment to anyone 13 | interested. 14 |

15 |
16 |
17 |

18 | Basically how it works is: 19 |

20 |
    21 |
  • 22 | You git push to a GitHub (or something else) server. 23 |
  • 24 |
  • 25 | The git server uses a WebHook to connect with a CI/CD system. 26 |
  • 27 |
  • The CI/CD thing builds your project, and deploys it somewhere.
  • 28 |
29 |
30 |
31 |

Simple, right? This webpage actually, was built with a Continuous Deployment script!

32 |
33 |
34 |

35 | 36 | Check out the source code 37 | {" "} 38 | to understand how it works. 39 |

40 |
41 |
42 |

43 | How does this project work? 44 |

45 |
46 |

Glad you asked!

47 |
48 |
    49 |
  • 50 | It was created with{" "} 51 | 52 | 53 | create-react-app 54 | 55 | . 56 |
  • 57 |
  • 58 | It uses Travis CI as the CI/CD thing, which starts a new build on{" "} 59 | git push. 60 |
  • 61 |
  • 62 | When Travis starts a new build, it executes{" "} 63 | this script. 64 | 72 |
  • 73 |
  • 74 | I highly recommend reading the deploy script above. It is heavily documented for you in true{" "} 75 | Basically fashion, in order to help you understand what's actually happening. 76 |
  • 77 |
  • 78 | After Travis is done, everything's deployed on{" "} 79 | 80 | gh-pages 81 | , and things are live. 82 |
  • 83 |
84 |
85 |

What's this about keys? 🗝

86 |
87 |

88 | Basically, Travis is going to be pushing to your GitHub account. Travis needs to be able to say HEY IM TEJAS 89 | in order to use Tejas' GitHub: it needs my key; my SSH key. 90 |

91 |
92 |
93 |

And so, I've got to:

94 |
    95 |
  • 96 | Generate some keys: 97 |
      98 |
    • 99 | In a terminal, type:
      100 | ssh-keygen -t rsa -b 4096 -C "hello@tej.as" # YOUR EMAIL HERE 101 |
    • 102 |
    • It'll ask you where to save it. Save the key somewhere familiar.
    • 103 |
    • It'll ask you for a passphrase. I usually leave this blank.
    • 104 |
    • 105 | It'll generate 2 files for you: 106 |
        107 |
      • 108 | One ending with .pub (make a note of this). 109 |
      • 110 |
      • And one with the name you gave it.
      • 111 |
      112 |
    • 113 |
    114 |
  • 115 |
  • 116 | Encrypt them with Travis: 117 |
      118 |
    • 119 | Make sure you have the{" "} 120 | 121 | Travis CLI 122 | {" "} 123 | installed. 124 |
    • 125 |
    • 126 | In the terminal, run:
      127 | travis encrypt-file WHATEVER_YOU_NAMED_YOUR_KEY 128 |
    • 129 |
    • 130 | It will then create a .enc file based your key's filename. 131 |
    • 132 |
    • 133 | It'll also say something back to you like:
      134 | 135 | openssl aes-256-cbc -K $encrypted_ 136 | 0a6446eb3ae3 137 | _key -iv $encrypted_0a6446eb3ae3_key -blah blah 138 | 139 |
    • 140 |
    • 141 | Copy the portion where we have green text above. You'll need 142 | it. 143 |
    • 144 |
    145 |
  • 146 |
  • 147 | Add them (the encrypted ones!) to my project: 148 |
      149 |
    • At this point, you have add your .enc file to your git repo and commit it.
    • 150 |
    • You can throw away your key at this point.
    • 151 |
    152 |
  • 153 |
  • 154 | Tell Travis how to decrypt them: 155 | 170 |
  • 171 |
  • 172 | Add the keys to my GitHub account: 173 |
      174 |
    • 175 | The last step is actually adding the public part of your key to your GitHub profile to say "yes, the 176 | Travis thing using my key is basically me". 177 |
    • 178 |
    • 179 | 180 | Go here 181 | , click the green New SSH Key, and paste the contents of your .pub file in the{" "} 182 | key field, giving it an appropriate title. 183 |
    • 184 |
    • Bam!
    • 185 |
    186 |
  • 187 |
188 |
189 |
190 |

191 | Whew! Now, Travis can properly push your shiny new gh-pages to your GitHub project. 192 |

193 |
194 |
195 | ) 196 | } 197 | } 198 | export default App 199 | --------------------------------------------------------------------------------