├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── pull_request.yml │ └── push.yaml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── __tests__ └── util.test.ts ├── action.yml ├── dist ├── elf_cam_bg.wasm ├── index.js └── thread.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── @types │ └── netlify.d.ts ├── main.ts └── util.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | dist 4 | lib 5 | coverage 6 | jest.config.js 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["jest", "@typescript-eslint"], 3 | "extends": ["plugin:github/recommended"], 4 | "parser": "@typescript-eslint/parser", 5 | "parserOptions": { 6 | "ecmaVersion": 9, 7 | "sourceType": "module", 8 | "project": "./tsconfig.json" 9 | }, 10 | "rules": { 11 | "camelcase": "off", 12 | "import/no-namespace": "off" 13 | }, 14 | "env": { 15 | "node": true, 16 | "es6": true, 17 | "jest/globals": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Common .gitattributes file courtesy of 2 | # https://github.com/alexkaratarakis/gitattributes/blob/master/Common.gitattributes 3 | 4 | # Auto detect text files and perform LF normalization 5 | * text=auto eol=lf 6 | 7 | # The above will handle all files NOT found below 8 | 9 | # Documents 10 | *.pdf diff=astextplain 11 | *.PDF diff=astextplain 12 | *.rtf diff=astextplain 13 | *.RTF diff=astextplain 14 | *.md text 15 | 16 | # Graphics 17 | *.png binary 18 | *.jpg binary 19 | *.jpeg binary 20 | *.gif binary 21 | *.tif binary 22 | *.tiff binary 23 | *.ico binary 24 | # SVG treated as an asset (binary) by default. 25 | *.svg binary 26 | #*.svg text 27 | *.eps binary 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Write a brief description of the changes introduced by this pull request. 4 | 5 | ### Related Issues 6 | 7 | Link to the issue that is fixed by this PR (if there is one) 8 | e.g. Fixes #1234, Addresses #1234, Related to #1234, etc. 9 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request CI 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | jobs: 10 | deployDraft: 11 | name: Deploy draft to Netlify 12 | runs-on: ubuntu-latest 13 | if: github.event_name == 'pull_request' && github.ref != 'refs/heads/master' 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@master 17 | 18 | - name: Generate simple HTML document 19 | run: | 20 | mkdir -p example 21 | echo -e "
$(date -u)\n$GITHUB_SHA\n$GITHUB_REF
" > example/index.html 22 | 23 | - name: Test action 24 | uses: ./ 25 | with: 26 | github-token: ${{ secrets.GITHUB_TOKEN }} 27 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 28 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 29 | build-dir: "./example" 30 | github-deployment-environment: "development" 31 | draft: true 32 | -------------------------------------------------------------------------------- /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | name: Push CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | lint: 7 | name: Lint 8 | runs-on: ubuntu-latest 9 | if: github.event_name == 'push' 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@master 13 | 14 | - name: Install dependencies 15 | run: npm ci 16 | 17 | - name: Lint 18 | run: npm run lint 19 | 20 | test: 21 | name: Test 22 | runs-on: ubuntu-latest 23 | if: github.event_name == 'push' 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@master 27 | 28 | - name: Install dependencies 29 | run: npm ci 30 | 31 | - name: Test 32 | run: npm run test 33 | env: 34 | CI: true 35 | 36 | dryRun: 37 | name: Dry run action 38 | runs-on: ubuntu-latest 39 | if: github.event_name == 'push' 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@master 43 | 44 | - name: Generate simple HTML document 45 | run: | 46 | mkdir -p example 47 | echo -e "
$(date -u)\n$GITHUB_SHA\n$GITHUB_REF
" > example/index.html 48 | 49 | - name: Test action 50 | uses: ./ 51 | with: 52 | github-token: ${{ secrets.GITHUB_TOKEN }} 53 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 54 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 55 | build-dir: "./example" 56 | comment-on-pull-request: true 57 | comment-on-commit: true 58 | github-deployment-environment: "development" 59 | draft: true 60 | dry-run: true 61 | 62 | deployTest: 63 | name: Deploy test to Netlify 64 | runs-on: ubuntu-latest 65 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 66 | steps: 67 | - name: Checkout repository 68 | uses: actions/checkout@master 69 | 70 | - name: Generate simple HTML document 71 | run: | 72 | mkdir -p example 73 | echo -e "
$(date -u)\n$GITHUB_SHA\n$GITHUB_REF
" > example/index.html 74 | 75 | - name: Test action 76 | uses: ./ 77 | with: 78 | github-token: ${{ secrets.GITHUB_TOKEN }} 79 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 80 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 81 | build-dir: "./example" 82 | comment-on-pull-request: true 83 | comment-on-commit: true 84 | github-deployment-environment: "test" 85 | draft: true 86 | 87 | deployProduction: 88 | name: Deploy production to Netlify 89 | runs-on: ubuntu-latest 90 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 91 | steps: 92 | - name: Checkout repository 93 | uses: actions/checkout@master 94 | 95 | - name: Generate simple HTML document 96 | run: | 97 | mkdir -p example 98 | echo -e "
$(date -u)\n$GITHUB_SHA\n$GITHUB_REF
" > example/index.html 99 | 100 | - name: Test action 101 | uses: ./ 102 | with: 103 | github-token: ${{ secrets.GITHUB_TOKEN }} 104 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 105 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 106 | build-dir: "./example" 107 | comment-on-commit: true 108 | github-deployment-environment: "production" 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # next.js build output 76 | .next 77 | 78 | # nuxt.js build output 79 | .nuxt 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # OS metadata 94 | .DS_Store 95 | Thumbs.db 96 | 97 | # Ignore built ts files 98 | __tests__/runner/* 99 | lib 100 | 101 | /dist/typescript* 102 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | dist 4 | lib 5 | coverage 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "printWidth": 120, 4 | "singleQuote": true, 5 | "trailingComma": "all" 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Alex Gabites 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 | # action-netlify-deploy 2 | 3 | 🙌 Netlify deployments via GitHub actions 4 | 5 | ![CI](https://github.com/South-Paw/action-netlify-deploy/workflows/CI/badge.svg) 6 | [![Dependencies](https://david-dm.org/South-Paw/action-netlify-deploy/status.svg)](https://david-dm.org/South-Paw/action-netlify-deploy) 7 | [![Dev Dependencies](https://david-dm.org/South-Paw/action-netlify-deploy/dev-status.svg)](https://david-dm.org/South-Paw/action-netlify-deploy?type=dev) 8 | 9 | ## About 10 | 11 | A Github action that deploys a build directory to Netlify. It can be configured to comment on the commit or PR with the deploy URL or deploy with GitHub environments, deploy draft builds, deploy Netlify functions and use custom Netlify configs. 12 | 13 | ## Getting started 14 | 15 | There are 4 required inputs for the action 16 | 17 | - `github-token` which is usually `${{ secrets.GITHUB_TOKEN }}` 18 | - `netlify-auth-token` this is a [personal access token](https://app.netlify.com/user/applications#personal-access-tokens) created from your Netlify account 19 | - ⚠️ This should be stored as a [Github secret](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) in your repo 20 | - `netlify-site-id` this is the API ID of your Netlify site 21 | - To find this, go to your site's settings and in the "site information" copy the `API ID` field 22 | - `build-dir` this is where your site build outputs to relative to the root of your projects folder 23 | 24 | See the [action.yml](./action.yml) for other action inputs and their descriptions 25 | 26 | ## Outputs 27 | 28 | ### `preview-url` 29 | 30 | The url of deployment preview. 31 | 32 | ### `preview-name` 33 | 34 | The name of deployment name. 35 | 36 | ## Usage 37 | 38 | Here are some ideas of how to configure this action in different workflows... 39 | 40 | - [Deploying drafts on each commit and publishing on push to master](https://github.com/South-Paw/action-netlify-deploy#deploying-drafts-on-each-commit-and-publishing-on-push-to-master) 41 | - [Deploying drafts of pull requests and publishing on push to master](https://github.com/South-Paw/action-netlify-deploy#deploying-drafts-of-pull-requests-and-publishing-on-push-to-master) 42 | - [Deploying drafts of pull requests and publish on release created](https://github.com/South-Paw/action-netlify-deploy#deploying-drafts-of-pull-requests-and-publish-on-release-created) 43 | 44 | ### Deploying drafts on each commit and publishing on push to master 45 | 46 | ```yaml 47 | name: CI 48 | 49 | on: [push] 50 | 51 | jobs: 52 | # This job will: 53 | # * deploy a draft every time there is a commit that is not on master branch 54 | # * comment on that commit with the deploy URL 55 | deployCommitDraft: 56 | name: Deploy draft to Netlify 57 | runs-on: ubuntu-latest 58 | if: github.event_name == 'push' && github.ref != 'refs/heads/master' 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@v1 62 | 63 | # ... your other build steps to produce a build directory 64 | # e.g. `npm run build` for create-react-app 65 | 66 | - name: Deploy draft to Netlify 67 | uses: South-Paw/action-netlify-deploy@v1.2.0 68 | with: 69 | github-token: ${{ secrets.GITHUB_TOKEN }} 70 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 71 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 72 | build-dir: './build' 73 | draft: true 74 | comment-on-commit: true 75 | 76 | # This job will: 77 | # * deploy a production build every time there is a push only on the master branch 78 | # * comment that commit with the deploy URL 79 | publishMasterCommit: 80 | name: Publish to Netlify 81 | runs-on: ubuntu-latest 82 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 83 | steps: 84 | - name: Checkout repository 85 | uses: actions/checkout@v1 86 | 87 | # ... your other build steps to produce a build directory 88 | # e.g. `npm run build` for create-react-app 89 | 90 | - name: Deploy production to Netlify 91 | uses: South-Paw/action-netlify-deploy@v1.2.0 92 | with: 93 | github-token: ${{ secrets.GITHUB_TOKEN }} 94 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 95 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 96 | build-dir: './build' 97 | comment-on-commit: true 98 | ``` 99 | 100 | ### Deploying drafts of pull requests and publishing on push to master 101 | 102 | ```yaml 103 | name: CI 104 | 105 | on: 106 | push: 107 | pull_request: 108 | types: 109 | - opened 110 | - synchronize 111 | 112 | jobs: 113 | # This job will: 114 | # * deploy a draft every time there is a pull request created or synchronized that is not on master branch 115 | # * comment on that pull request with the deploy URL 116 | deployPRDraft: 117 | name: Deploy draft to Netlify 118 | runs-on: ubuntu-latest 119 | if: github.event_name == 'pull_request' && github.ref != 'refs/heads/master' 120 | steps: 121 | - name: Checkout repository 122 | uses: actions/checkout@v1 123 | 124 | # ... your other build steps to produce a build directory 125 | # e.g. `npm run build` for create-react-app 126 | 127 | - name: Deploy draft to Netlify 128 | uses: South-Paw/action-netlify-deploy@v1.2.0 129 | with: 130 | github-token: ${{ secrets.GITHUB_TOKEN }} 131 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 132 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 133 | build-dir: './build' 134 | draft: true 135 | comment-on-pull-request: true 136 | 137 | # This job will: 138 | # * deploy a production build every time there is a push on the master branch 139 | # * comment that commit with the deploy URL 140 | publishMasterCommit: 141 | name: Publish to Netlify 142 | runs-on: ubuntu-latest 143 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 144 | steps: 145 | - name: Checkout repository 146 | uses: actions/checkout@v1 147 | 148 | # ... your other build steps to produce a build directory 149 | # e.g. `npm run build` for create-react-app 150 | 151 | - name: Deploy production to Netlify 152 | uses: South-Paw/action-netlify-deploy@v1.2.0 153 | with: 154 | github-token: ${{ secrets.GITHUB_TOKEN }} 155 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 156 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 157 | build-dir: './build' 158 | comment-on-commit: true 159 | ``` 160 | 161 | ### Deploying drafts of pull requests and publish on release created 162 | 163 | ```yaml 164 | name: CI 165 | 166 | on: 167 | pull_request: 168 | types: 169 | - opened 170 | - synchronize 171 | release: 172 | types: 173 | - created 174 | 175 | jobs: 176 | # This job will: 177 | # * deploy a draft every time there is a pull request created or synchronized that is not on master branch 178 | # * comment on that pull request with the deploy URL 179 | deployPRDraft: 180 | name: Deploy draft to Netlify 181 | runs-on: ubuntu-latest 182 | if: github.event_name == 'pull_request' && github.ref != 'refs/heads/master' 183 | steps: 184 | - name: Checkout repository 185 | uses: actions/checkout@v1 186 | 187 | # ... your other build steps to produce a build directory 188 | # e.g. `npm run build` for create-react-app 189 | 190 | - name: Deploy draft to Netlify 191 | uses: South-Paw/action-netlify-deploy@v1.2.0 192 | with: 193 | github-token: ${{ secrets.GITHUB_TOKEN }} 194 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 195 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 196 | build-dir: './build' 197 | draft: true 198 | comment-on-pull-request: true 199 | 200 | # This job will: 201 | # * deploy a production build every time there is a release created on the master branch 202 | # * comment that commit with the deploy URL 203 | publishOnMasterRelease: 204 | name: Publish release to Netlify 205 | runs-on: ubuntu-latest 206 | if: github.event_name == 'release' && github.event.action == 'created' 207 | steps: 208 | - name: Checkout repository 209 | uses: actions/checkout@v1 210 | 211 | # ... your other build steps to produce a build directory 212 | # e.g. `npm run build` for create-react-app 213 | 214 | - name: Deploy production to Netlify 215 | uses: South-Paw/action-netlify-deploy@v1.2.0 216 | with: 217 | github-token: ${{ secrets.GITHUB_TOKEN }} 218 | netlify-auth-token: ${{ secrets.NETLIFY_AUTH_TOKEN }} 219 | netlify-site-id: ${{ secrets.NETLIFY_SITE_ID }} 220 | build-dir: './build' 221 | ``` 222 | 223 | ## Issues and Bugs 224 | 225 | If you find any, please report them [here](https://github.com/South-Paw/action-report-coverage/issues) so they can be squashed. 226 | 227 | ## Development and Contributing 228 | 229 | Download the repo and then install dependencies with `npm`. 230 | 231 | ```bash 232 | # build the action to `./dist` 233 | npm run build 234 | 235 | # clean the `./dist` dir 236 | npm run clean 237 | 238 | # lint project 239 | npm run lint 240 | 241 | # run tests 242 | npm run test 243 | ``` 244 | 245 | Contributions and improvements are always welcome 👍 246 | 247 | ## License 248 | 249 | MIT, see the [LICENSE](./LICENSE) file. 250 | -------------------------------------------------------------------------------- /__tests__/util.test.ts: -------------------------------------------------------------------------------- 1 | import { createCommentMessage, NetlifyDeploy } from '../src/util'; 2 | 3 | const mockDeploy: NetlifyDeploy = { 4 | id: 'string', 5 | site_id: 'string', 6 | user_id: 'string', 7 | build_id: 'string', 8 | state: 'string', 9 | name: 'string', 10 | url: 'string', 11 | ssl_url: 'string', 12 | admin_url: 'string', 13 | deploy_url: 'string', 14 | deploy_ssl_url: 'string', 15 | screenshot_url: 'string', 16 | review_id: 0, 17 | draft: true, 18 | required: [], 19 | required_functions: [], 20 | error_message: 'string', 21 | branch: 'string', 22 | commit_ref: 'string', 23 | commit_url: 'string', 24 | skipped: false, 25 | created_at: 'string', 26 | updated_at: 'string', 27 | published_at: 'string', 28 | title: 'string', 29 | context: 'string', 30 | locked: false, 31 | review_url: 'string', 32 | site_capabilities: {}, 33 | }; 34 | 35 | describe('createCommentMessage', () => { 36 | it('should return a draft string when `isDraft` is true', () => { 37 | const string = createCommentMessage(true, { ...mockDeploy, deploy_ssl_url: 'https://example.com' }); 38 | expect(string).toContain('https://example.com'); 39 | }); 40 | 41 | it('should return a published string when `isDraft` is false', () => { 42 | const string = createCommentMessage(false, { ...mockDeploy, name: 'Testing name', ssl_url: 'https://example.com' }); 43 | expect(string).toContain('Testing name'); 44 | expect(string).toContain('https://example.com'); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: Netlify Deploy Action 2 | description: Netlify deployments via GitHub actions 3 | author: Alex Gabites 4 | branding: 5 | color: purple 6 | icon: package 7 | runs: 8 | using: "node12" 9 | main: "dist/index.js" 10 | inputs: 11 | # Required inputs 12 | github-token: 13 | description: GitHub token 14 | required: true 15 | netlify-auth-token: 16 | description: Your Netlify personal access token (Please save it as a secret in your repo!) 17 | required: true 18 | netlify-site-id: 19 | description: Your Netlify site's `API ID` 20 | required: true 21 | build-dir: 22 | description: The folder of the site want to deploy 23 | required: true 24 | # Config inputs 25 | comment-on-commit: 26 | description: If `true` AND the context of the action is a `commit` then the action will comment on the SHA. 27 | required: false 28 | default: "false" 29 | comment-on-pull-request: 30 | description: If `true` AND the context of the action is a `pull_request` then the action will comment on the PR. 31 | required: false 32 | default: "false" 33 | github-deployment-environment: 34 | description: The name of the GitHub environment to deploy to. 35 | required: false 36 | github-deployment-description: 37 | description: Optional description to add to the deployment. 38 | required: false 39 | github-deployment-is-transient: 40 | description: Specifies if the given environment is specific to the deployment and will no longer exist at some point in the future. 41 | required: false 42 | default: "false" 43 | github-deployment-is-production: 44 | description: Specifies if the given environment is one that end-users directly interact with. 45 | required: false 46 | default: "true" 47 | github-deployment-should-report-status: 48 | description: If `true` AND `github-env` is set then the action will report the status of environment deployments on pull requests. 49 | required: false 50 | default: "true" 51 | dry-run: 52 | description: Run the action but don't actually deploy to Netlify or comment on the commit/PR 53 | required: false 54 | default: "false" 55 | # Other optional inputs (see: https://github.com/netlify/js-client#deploy--await-clientdeploysiteid-builddir-opts) 56 | functions-dir: 57 | description: The folder of any functions to deploy 58 | required: false 59 | config-path: 60 | description: Path to a netlify.toml file to include in the deploy 61 | required: false 62 | draft: 63 | description: If it is a draft deploy or production deploy 64 | required: false 65 | default: "false" 66 | message: 67 | description: "A short message to associate with the deploy (Note: setting this will override the default deploy message of `: [short_sha]`)" 68 | required: false 69 | outputs: 70 | preview-url: 71 | description: "deployment preview URL" 72 | preview-name: 73 | description: "deployment preview name" 74 | -------------------------------------------------------------------------------- /dist/elf_cam_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/South-Paw/action-netlify-deploy/2e5ce944afb065f54453c5cc03291670687b454b/dist/elf_cam_bg.wasm -------------------------------------------------------------------------------- /dist/thread.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | const crypto = require('crypto'); 4 | const {parentPort} = require('worker_threads'); 5 | 6 | const handlers = { 7 | hashFile: (algorithm, filePath) => new Promise((resolve, reject) => { 8 | const hasher = crypto.createHash(algorithm); 9 | fs.createReadStream(filePath) 10 | // TODO: Use `Stream.pipeline` when targeting Node.js 12. 11 | .on('error', reject) 12 | .pipe(hasher) 13 | .on('error', reject) 14 | .on('finish', () => { 15 | const {buffer} = new Uint8Array(hasher.read()); 16 | resolve({value: buffer, transferList: [buffer]}); 17 | }); 18 | }), 19 | hash: async (algorithm, input) => { 20 | const hasher = crypto.createHash(algorithm); 21 | 22 | if (Array.isArray(input)) { 23 | for (const part of input) { 24 | hasher.update(part); 25 | } 26 | } else { 27 | hasher.update(input); 28 | } 29 | 30 | const {buffer} = new Uint8Array(hasher.digest()); 31 | return {value: buffer, transferList: [buffer]}; 32 | } 33 | }; 34 | 35 | parentPort.on('message', async message => { 36 | try { 37 | const {method, args} = message; 38 | const handler = handlers[method]; 39 | 40 | if (handler === undefined) { 41 | throw new Error(`Unknown method '${method}'`); 42 | } 43 | 44 | const {value, transferList} = await handler(...args); 45 | parentPort.postMessage({id: message.id, value}, transferList); 46 | } catch (error) { 47 | const newError = {message: error.message, stack: error.stack}; 48 | 49 | for (const [key, value] of Object.entries(error)) { 50 | if (typeof value !== 'object') { 51 | newError[key] = value; 52 | } 53 | } 54 | 55 | parentPort.postMessage({id: message.id, error: newError}); 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleFileExtensions: ['js', 'ts'], 4 | testEnvironment: 'node', 5 | testMatch: ['**/*.test.ts'], 6 | testRunner: 'jest-circus/runner', 7 | transform: { 8 | '^.+\\.ts$': 'ts-jest', 9 | }, 10 | verbose: true, 11 | }; 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@south-paw/action-netlify-deploy", 3 | "version": "1.2.1", 4 | "description": "Netlify deployments via GitHub actions", 5 | "keywords": [], 6 | "homepage": "https://github.com/South-Paw/action-netlify-deploy", 7 | "bugs": "https://github.com/South-Paw/action-netlify-deploy/issues", 8 | "license": "MIT", 9 | "author": { 10 | "name": "Alex Gabites", 11 | "email": "hello@southpaw.co.nz", 12 | "url": "http://southpaw.co.nz/" 13 | }, 14 | "main": "lib/main.js", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/South-Paw/action-netlify-deploy.git" 18 | }, 19 | "scripts": { 20 | "clean": "rimraf dist lib", 21 | "build:tsc": "tsc", 22 | "build:ncc": "ncc build", 23 | "build": "npm run clean && npm run build:tsc && npm run build:ncc", 24 | "lint": "npm run lint:type-check && npm run lint:eslint", 25 | "lint:eslint": "eslint \"src/**/*.{js,ts}\"", 26 | "lint:type-check": "tsc --noEmit", 27 | "test": "jest", 28 | "test:coverage": "jest --coverage" 29 | }, 30 | "dependencies": { 31 | "@actions/core": "^1.2.6", 32 | "@actions/github": "^4.0.0", 33 | "netlify": "^6.1.9" 34 | }, 35 | "devDependencies": { 36 | "@types/jest": "^26.0.20", 37 | "@types/node": "^14.14.31", 38 | "@typescript-eslint/parser": "^4.15.1", 39 | "@vercel/ncc": "^0.25.1", 40 | "eslint": "^7.20.0", 41 | "eslint-plugin-github": "^4.1.1", 42 | "eslint-plugin-jest": "^24.1.5", 43 | "jest": "^26.6.3", 44 | "jest-circus": "^26.6.3", 45 | "prettier": "2.2.1", 46 | "rimraf": "^3.0.2", 47 | "ts-jest": "^26.5.1", 48 | "typescript": "^4.1.5" 49 | }, 50 | "private": true 51 | } 52 | -------------------------------------------------------------------------------- /src/@types/netlify.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'netlify'; 2 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core'; 2 | import * as github from '@actions/github'; 3 | import NetlifyAPI from 'netlify'; 4 | import * as path from 'path'; 5 | import { createCommentMessage, getDeployUrl } from './util'; 6 | 7 | async function run(): Promise<void> { 8 | try { 9 | const isCommit = Object.keys(github.context.payload).includes('head_commit'); 10 | const isPullRequest = Object.keys(github.context.payload).includes('pull_request'); 11 | const isRelease = Object.keys(github.context.payload).includes('release'); 12 | 13 | const { 14 | payload, 15 | sha, 16 | issue: { number }, 17 | repo: { owner, repo }, 18 | } = github.context; 19 | 20 | const shaShort = sha.slice(0, 7); 21 | const deploymentSha = payload.pull_request?.head.sha ?? sha; 22 | const commitMessage = isCommit ? payload.head_commit?.message : undefined; 23 | const pullRequestNumber = payload.pull_request?.number; 24 | const pullRequestTitle = isPullRequest ? payload.pull_request?.title : undefined; 25 | const releaseTag = isRelease ? payload.release?.tag_name : undefined; 26 | const releaseTitle = isRelease ? payload.release?.name : undefined; 27 | 28 | // Get required inputs 29 | const githubToken = core.getInput('github-token', { required: true }); 30 | const netlifyAuthToken = core.getInput('netlify-auth-token', { required: true }); 31 | const siteId = core.getInput('netlify-site-id', { required: true }); 32 | const buildDir = core.getInput('build-dir', { required: true }); 33 | 34 | // Get config inputs 35 | const commentOnCommit = core.getInput('comment-on-commit') === 'true'; 36 | const commentOnPullRequest = core.getInput('comment-on-pull-request') === 'true'; 37 | const githubDeployEnvironment = core.getInput('github-deployment-environment') || undefined; 38 | const githubDeployDescription = core.getInput('github-deployment-description') || undefined; 39 | const githubDeployIsTransient = core.getInput('github-deployment-is-transient') === 'true'; 40 | const githubDeployIsProduction = core.getInput('github-deployment-is-production') === 'true'; 41 | const githubDeployReportStatus = core.getInput('github-deployment-should-report-status') === 'true'; 42 | const dryRun = core.getInput('dry-run') === 'true'; 43 | 44 | // Get optional inputs 45 | const configPath = core.getInput('config-path') || undefined; 46 | const draft = core.getInput('draft') === 'true'; 47 | const functionsDir = core.getInput('functions-dir') || undefined; 48 | let message = core.getInput('message'); 49 | 50 | // Create clients 51 | const githubClient = github.getOctokit(githubToken); 52 | const netlifyClient = new NetlifyAPI(netlifyAuthToken); 53 | 54 | // If there's no explict deploy message input, then make a deploy message from the action's context. 55 | if (!message) { 56 | message = `Build [${shaShort}]`; 57 | 58 | if (isCommit) { 59 | message = `Commit: ${commitMessage} [${shaShort}]`; 60 | } 61 | 62 | if (isPullRequest) { 63 | message = `PR: ${pullRequestTitle} [${shaShort}]`; 64 | } 65 | 66 | if (isRelease) { 67 | message = `Release: ${releaseTitle} [${releaseTag}]`; 68 | } 69 | } 70 | 71 | if (dryRun) { 72 | process.stdout.write(`Action is running dry - there won't be any outputs from this run.\n`); 73 | } 74 | 75 | process.stdout.write(`Deploying ${draft ? 'draft ' : ''}to Netlify...\n`); 76 | 77 | let deploy; 78 | 79 | if (!dryRun) { 80 | try { 81 | const siteDir = path.resolve(process.cwd(), buildDir); 82 | const fnDir = functionsDir ? path.resolve(process.cwd(), functionsDir) : undefined; 83 | 84 | const deployment = await netlifyClient.deploy(siteId, siteDir, { configPath, draft, fnDir, message }); 85 | 86 | deploy = deployment.deploy; 87 | core.setOutput('preview-name', deploy.name); 88 | core.setOutput('preview-url', getDeployUrl(draft, deploy)); 89 | } catch (error) { 90 | process.stderr.write('netlifyClient.deploy() failed\n'); 91 | process.stderr.write(`${JSON.stringify(error, null, 2)}\n`); 92 | core.setFailed(error.message); 93 | } 94 | 95 | if (!deploy) { 96 | core.setFailed('Failed to deploy to Netlify!'); 97 | return; 98 | } 99 | } else { 100 | process.stdout.write(`[Dry run] Netlify deploy message: "${message}"\n`); 101 | } 102 | 103 | const body = dryRun 104 | ? createCommentMessage(draft, { 105 | name: 'dry-run', 106 | deploy_ssl_url: 'http://example.com', 107 | ssl_url: 'http://example.com', 108 | }) 109 | : createCommentMessage(draft, deploy); 110 | 111 | if (isCommit && commentOnCommit) { 112 | process.stdout.write(`Commenting on commit ${shaShort} (SHA: ${sha})\n`); 113 | 114 | if (!dryRun) { 115 | try { 116 | await githubClient.repos.createCommitComment({ 117 | owner, 118 | repo, 119 | commit_sha: sha, 120 | body, 121 | }); 122 | } catch (error) { 123 | process.stderr.write('creating commit comment failed\n'); 124 | process.stderr.write(`${JSON.stringify(error, null, 2)}\n`); 125 | core.setFailed(error.message); 126 | } 127 | } else { 128 | process.stdout.write(`[Dry run] GitHub commit comment: "${body}"\n`); 129 | } 130 | } 131 | 132 | if (isPullRequest && commentOnPullRequest) { 133 | process.stdout.write(`Commenting on pull request #${pullRequestNumber}\n`); 134 | 135 | if (!dryRun) { 136 | try { 137 | await githubClient.issues.createComment({ 138 | owner, 139 | repo, 140 | issue_number: number, 141 | body, 142 | }); 143 | } catch (error) { 144 | process.stderr.write('creating pull request comment failed\n'); 145 | process.stderr.write(`${JSON.stringify(error, null, 2)}\n`); 146 | core.setFailed(error.message); 147 | } 148 | } else { 149 | process.stdout.write(`[Dry run] GitHub pull request comment: "${body}"\n`); 150 | } 151 | } 152 | 153 | if (githubDeployEnvironment) { 154 | if (!dryRun) { 155 | process.stdout.write(`Creating deployment for "${githubDeployEnvironment}"\n`); 156 | 157 | try { 158 | const deployment = await githubClient.repos.createDeployment({ 159 | owner, 160 | repo, 161 | ref: deploymentSha, 162 | auto_merge: false, 163 | required_contexts: [], 164 | environment: githubDeployEnvironment, 165 | description: githubDeployDescription, 166 | transient_environment: githubDeployIsTransient, 167 | production_environment: githubDeployIsProduction, 168 | }); 169 | 170 | await githubClient.repos.createDeploymentStatus({ 171 | owner, 172 | repo, 173 | deployment_id: deployment.data.id, 174 | state: 'success', 175 | environment_url: getDeployUrl(draft, deploy), 176 | }); 177 | } catch (error) { 178 | process.stderr.write('creating deployment failed\n'); 179 | process.stderr.write(`${JSON.stringify(error, null, 2)}\n`); 180 | core.setFailed(error.message); 181 | } 182 | } else { 183 | process.stdout.write(`[Dry run] GitHub deployment env: "${githubDeployEnvironment}"\n`); 184 | } 185 | 186 | if (!dryRun) { 187 | if (githubDeployReportStatus) { 188 | process.stdout.write(`Creating commit status for SHA: "${deploymentSha}"\n`); 189 | 190 | try { 191 | await githubClient.repos.createCommitStatus({ 192 | sha: deploymentSha, 193 | owner, 194 | repo, 195 | state: 'success', 196 | context: 'action-netlify-deploy', 197 | target_url: getDeployUrl(draft, deploy), 198 | description: 'action-netlify-deploy status', 199 | }); 200 | } catch (error) { 201 | process.stderr.write('creating commit status failed\n'); 202 | process.stderr.write(`${JSON.stringify(error, null, 2)}\n`); 203 | core.setFailed(error.message); 204 | } 205 | } 206 | } else { 207 | process.stdout.write(`[Dry run] GitHub commit status "success" on "${deploymentSha}"\n`); 208 | } 209 | } 210 | } catch (error) { 211 | process.stderr.write(JSON.stringify(error, null, 2)); 212 | core.setFailed(error.message); 213 | } 214 | } 215 | 216 | run(); 217 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Netlify `createSiteDeploy` response. 3 | * 4 | * @see https://open-api.netlify.com/#operation/createSiteDeploy 5 | */ 6 | export interface NetlifyDeploy { 7 | id: string; 8 | site_id: string; 9 | user_id: string; 10 | build_id: string; 11 | state: string; 12 | name: string; 13 | url: string; 14 | ssl_url: string; 15 | admin_url: string; 16 | deploy_url: string; 17 | deploy_ssl_url: string; 18 | screenshot_url: string; 19 | review_id: number; 20 | draft: boolean; 21 | required: string[]; 22 | required_functions: string[]; 23 | error_message: string; 24 | branch: string; 25 | commit_ref: string; 26 | commit_url: string; 27 | skipped: boolean; 28 | created_at: string; 29 | updated_at: string; 30 | published_at: string; 31 | title: string; 32 | context: string; 33 | locked: boolean; 34 | review_url: string; 35 | site_capabilities: object; 36 | } 37 | 38 | export const getDeployUrl = (isDraft: boolean, deploy: any): string => 39 | isDraft ? deploy.deploy_ssl_url : deploy.ssl_url; 40 | 41 | export const createCommentMessage = (isDraft: boolean, deploy: any): string => 42 | isDraft 43 | ? `🚀 Netlify deployed **${deploy.name}** as draft\n\n${getDeployUrl(isDraft, deploy)}` 44 | : `🎉 Netlify deployed **${deploy.name}** as production\n\n${getDeployUrl(isDraft, deploy)}`; 45 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./lib", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "esModuleInterop": true 10 | }, 11 | "exclude": ["dist", "lib", "node_modules", "**/*.test.ts"] 12 | } 13 | --------------------------------------------------------------------------------