├── .eslintignore
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── ftp.yml
│ └── ftps.yml
├── .gitignore
├── LICENSE
├── README.md
├── action.yml
├── dist
└── index.js
├── images
├── ftp-deploy-logo-full.png
└── ftp-deploy-logo-small.png
├── migration.md
├── package-lock.json
├── package.json
├── project.code-workspace
├── src
├── main.test.ts
├── main.ts
└── parse.ts
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/
2 | node_modules/
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "jest",
4 | "@typescript-eslint"
5 | ],
6 | "parser": "@typescript-eslint/parser",
7 | "parserOptions": {
8 | "ecmaVersion": 8,
9 | "sourceType": "module",
10 | "project": "./tsconfig.json"
11 | },
12 | "rules": {
13 | "eslint-comments/no-use": "off",
14 | "import/no-namespace": "off",
15 | "no-unused-vars": "off",
16 | "@typescript-eslint/no-unused-vars": "error",
17 | "@typescript-eslint/explicit-member-accessibility": [
18 | "error",
19 | {
20 | "accessibility": "no-public"
21 | }
22 | ],
23 | "@typescript-eslint/no-require-imports": "error",
24 | "@typescript-eslint/array-type": "error",
25 | "@typescript-eslint/await-thenable": "error",
26 | "@typescript-eslint/func-call-spacing": [
27 | "error",
28 | "never"
29 | ],
30 | "@typescript-eslint/no-array-constructor": "error",
31 | "@typescript-eslint/no-empty-interface": "error",
32 | "@typescript-eslint/no-extraneous-class": "error",
33 | "@typescript-eslint/no-for-in-array": "error",
34 | "@typescript-eslint/no-inferrable-types": "error",
35 | "@typescript-eslint/no-misused-new": "error",
36 | "@typescript-eslint/no-namespace": "error",
37 | "@typescript-eslint/no-non-null-assertion": "warn",
38 | "@typescript-eslint/no-unnecessary-qualifier": "error",
39 | "@typescript-eslint/no-unnecessary-type-assertion": "error",
40 | "@typescript-eslint/no-useless-constructor": "error",
41 | "@typescript-eslint/no-var-requires": "error",
42 | "@typescript-eslint/prefer-for-of": "warn",
43 | "@typescript-eslint/prefer-function-type": "warn",
44 | "@typescript-eslint/prefer-includes": "error",
45 | "@typescript-eslint/prefer-string-starts-ends-with": "error",
46 | "@typescript-eslint/promise-function-async": "error",
47 | "@typescript-eslint/require-array-sort-compare": "error",
48 | "@typescript-eslint/restrict-plus-operands": "error",
49 | "@typescript-eslint/type-annotation-spacing": "error"
50 | },
51 | "env": {
52 | "node": true,
53 | "es6": true,
54 | "jest/globals": true
55 | }
56 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Bug Description**
11 | A clear and concise description of what the bug is.
12 |
13 | **My Action Config**
14 | ```yaml
15 | on: push
16 | name: Publish Website
17 | jobs:
18 | web-deploy:
19 | name: 🚀 Deploy website every commit
20 | # !!!!!!! TODO Fill Out !!!!!!!
21 | ```
22 |
23 | **My Action Log**
24 | ```
25 | # Paste Log here
26 | # you may want enable verbose logging with log-level: verbose
27 | ```
28 |
--------------------------------------------------------------------------------
/.github/workflows/ftp.yml:
--------------------------------------------------------------------------------
1 | name: FTP Test
2 |
3 | on:
4 | push:
5 | branches: [master]
6 |
7 | jobs:
8 | deploy:
9 | name: 🚀 Deploy website every commit
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: 🚚 Get latest code
13 | uses: actions/checkout@v4
14 |
15 | - name: 📂 Sync files
16 | uses: ./
17 | with:
18 | server: ftp.samkirkland.com
19 | username: test@samkirkland.com
20 | password: ${{ secrets.ftp_password }}
21 |
--------------------------------------------------------------------------------
/.github/workflows/ftps.yml:
--------------------------------------------------------------------------------
1 | name: FTPS Test
2 |
3 | on:
4 | push:
5 | branches: [master]
6 |
7 | jobs:
8 | deploy:
9 | name: 🚀 Deploy website every commit
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: 🚚 Get latest code
13 | uses: actions/checkout@v4
14 |
15 | - name: 📂 Sync files
16 | uses: ./
17 | with:
18 | server: ftp.samkirkland.com
19 | username: test@samkirkland.com
20 | password: ${{ secrets.ftp_password }}
21 | protocol: ftps
22 |
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2018 GitHub, Inc. and contributors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Automate deploying websites and more with this GitHub action. **It's free!**
6 |
7 | 
8 | 
9 |
10 | ---
11 |
12 | ### Usage Example
13 | Place the following in `/.github/workflows/main.yml`
14 | ```yml
15 | on: push
16 | name: 🚀 Deploy website on push
17 | jobs:
18 | web-deploy:
19 | name: 🎉 Deploy
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: 🚚 Get latest code
23 | uses: actions/checkout@v4
24 |
25 | - name: 📂 Sync files
26 | uses: SamKirkland/FTP-Deploy-Action@v4.3.5
27 | with:
28 | server: ftp.samkirkland.com
29 | username: myFtpUserName
30 | password: ${{ secrets.ftp_password }}
31 | ```
32 |
33 | ---
34 |
35 | ### Requirements
36 | - You must have ftp access to your server. If your host allows or requires ssh please use my [web-deploy](https://github.com/SamKirkland/web-deploy) action
37 | - Some web hosts change the default port (21), check with your host for your port number
38 |
39 | ---
40 |
41 | ### Setup Steps
42 | 1. Select the repository you want to add the action to
43 | 2. Select the `Actions` tab
44 | 3. Select `Blank workflow file` or `Set up a workflow yourself`, if you don't see these options manually create a yaml file `Your_Project/.github/workflows/main.yml`
45 | 4. Paste the example above into your yaml file and save
46 | 5. Now you need to add a key to the `secrets` section in your project. To add a `secret` go to the `Settings` tab in your project then select `Secrets`. Add a new `Secret` for `password`
47 | 6. Update your yaml file settings
48 | 7. If you appreciate this github action give it a :star: or show off with one of the [badges below](#badge).
49 |
50 | ---
51 |
52 | ### Settings
53 | Keys can be added directly to your .yml config file or referenced from your project `Secrets` storage.
54 |
55 | To add a `secret` go to the `Settings` tab in your project then select `Secrets`.
56 | I strongly recommend you store your `password` as a secret.
57 |
58 | | Key Name | Required | Example | Default Value | Description |
59 | |-------------------------|----------|-------------------------------|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
60 | | `server` | Yes | `ftp.samkirkland.com` | | Deployment destination server |
61 | | `username` | Yes | `username@samkirkland.com` | | FTP user name |
62 | | `password` | Yes | `CrazyUniquePassword&%123` | | FTP password, be sure to escape quotes and spaces |
63 | | `port` | No | `990` | `21` | Server port to connect to (read your web hosts docs) |
64 | | `protocol` | No | `ftps` | `ftp` | `ftp`: provides no encryption, `ftps`: full encryption newest standard (aka "explicit" ftps), `ftps-legacy`: full encryption legacy standard (aka "implicit" ftps) |
65 | | `local-dir` | No | `./myFolderToPublish/` | `./` | Folder to upload from, must end with trailing slash `/` |
66 | | `server-dir` | No | `public_html/www/` | `./` | Folder to upload to (on the server), must end with trailing slash `/` |
67 | | `state-name` | No | `folder/.sync-state.json` | `.ftp-deploy-sync-state.json` | Path and name of the state file - this file is used to track which files have been deployed |
68 | | `dry-run` | No | `true` | `false` | Prints which modifications will be made with current config options, but doesn't actually make any changes |
69 | | `dangerous-clean-slate` | No | `true` | `false` | Deletes ALL contents of server-dir, even items in excluded with 'exclude' argument |
70 | | `exclude` | No | [See Example](#exclude-files) | [See Example](#exclude-files) | An array of glob patterns, these files will not be included in the publish/delete process. [List MUST be in this format](#exclude-files). You can use [a glob tester](https://www.digitalocean.com/community/tools/glob?comments=true&glob=%2A%2A%2F.git%2A%2F%2A%2A&matches=false&tests=test%2Fsam&tests=.git%2F%0D&tests=.github%2F%0D&tests=.git%2Ftest%0D&tests=.gitattributes%0D&tests=.gitignore%0D&tests=.git%2Fconfig%0D&tests=.git%2Ftest%2Ftest&tests=.github%2Fworkflows%2Fmain.yml&tests=node_modules%2Ffolder%2F%0D&tests=node_modules%2Fotherfolder%2F%0D&tests=subfolder%2Fnode_modules%2F) to test your pattern(s). |
71 | | `log-level` | No | `minimal` | `standard` | `minimal`: only important info, `standard`: important info and basic file changes, `verbose`: print everything the script is doing |
72 | | `security` | No | `strict` | `loose` | `strict`: Reject any connection which is not authorized with the list of supplied CAs. `loose`: Allow connection even when the domain is not certificate |
73 | | `timeout` | No | `60000` | `30000` | Timeout in milliseconds for FTP operations |
74 |
75 |
76 | # Common Examples
77 | #### Build and Publish React/Angular/Vue Website
78 | Make sure you have an npm script named 'build'. This config should work for most node built websites.
79 |
80 | ```yml
81 | on: push
82 | name: 🚀 Deploy website on push
83 | jobs:
84 | web-deploy:
85 | name: 🎉 Deploy
86 | runs-on: ubuntu-latest
87 | steps:
88 | - name: 🚚 Get latest code
89 | uses: actions/checkout@v4
90 |
91 | - name: Use Node.js 16
92 | uses: actions/setup-node@v2
93 | with:
94 | node-version: '16'
95 |
96 | - name: 🔨 Build Project
97 | run: |
98 | npm install
99 | npm run build
100 |
101 | - name: 📂 Sync files
102 | uses: SamKirkland/FTP-Deploy-Action@v4.3.5
103 | with:
104 | server: ftp.samkirkland.com
105 | username: myFtpUserName
106 | password: ${{ secrets.password }}
107 | ```
108 |
109 | #### FTPS
110 | ```yml
111 | on: push
112 | name: 🚀 Deploy website on push
113 | jobs:
114 | web-deploy:
115 | name: 🎉 Deploy
116 | runs-on: ubuntu-latest
117 | steps:
118 | - name: 🚚 Get latest code
119 | uses: actions/checkout@v4
120 |
121 | - name: 📂 Sync files
122 | uses: SamKirkland/FTP-Deploy-Action@v4.3.5
123 | with:
124 | server: ftp.samkirkland.com
125 | username: myFtpUserName
126 | password: ${{ secrets.password }}
127 | protocol: ftps
128 | port: 1234 # todo replace with your web hosts ftps port
129 | ```
130 |
131 | #### Log only dry run: Use this option for testing
132 | Ouputs a list of files that will be created/modified to sync your source without making any actual changes
133 | ```yml
134 | on: push
135 | name: 🚀 Deploy website on push
136 | jobs:
137 | web-deploy:
138 | name: 🎉 Deploy
139 | runs-on: ubuntu-latest
140 | steps:
141 | - name: 🚚 Get latest code
142 | uses: actions/checkout@v4
143 |
144 | - name: 📂 Sync files
145 | uses: SamKirkland/FTP-Deploy-Action@v4.3.5
146 | with:
147 | server: ftp.samkirkland.com
148 | username: myFtpUserName
149 | password: ${{ secrets.password }}
150 | dry-run: true
151 | ```
152 |
153 | #### Exclude files
154 | Excludes files
155 | ```yml
156 | on: push
157 | name: 🚀 Deploy website on push
158 | jobs:
159 | web-deploy:
160 | name: 🎉 Deploy
161 | runs-on: ubuntu-latest
162 | steps:
163 | - name: 🚚 Get latest code
164 | uses: actions/checkout@v4
165 |
166 | - name: 📂 Sync files
167 | uses: SamKirkland/FTP-Deploy-Action@v4.3.5
168 | with:
169 | server: ftp.samkirkland.com
170 | username: myFtpUserName
171 | password: ${{ secrets.password }}
172 | exclude: |
173 | **/.git*
174 | **/.git*/**
175 | **/node_modules/**
176 | fileToExclude.txt
177 | ```
178 |
179 | `exclude` has the following default value
180 | ```yml
181 | exclude: |
182 | **/.git*
183 | **/.git*/**
184 | **/node_modules/**
185 | ```
186 | if you overwrite the default value you will probably want to respecify them
187 |
188 |
189 | ---
190 |
191 | _Want another example? Let me know by creating a [github issue](https://github.com/SamKirkland/FTP-Deploy-Action/issues/new)_
192 |
193 | ---
194 |
195 | ## Badge
196 |
197 | If you appreciate this github action give it a :star: or show off with one of the badges below. Feel free to edit the text or color.
198 |
199 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
200 |
201 | ```md
202 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
203 | ```
204 |
205 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
206 |
207 | ```md
208 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
209 | ```
210 |
211 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
212 |
213 | ```md
214 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
215 | ```
216 |
217 | ---
218 |
219 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
220 |
221 | ```md
222 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
223 | ```
224 |
225 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
226 |
227 | ```md
228 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
229 | ```
230 |
231 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
232 |
233 | ```md
234 | [
](https://github.com/SamKirkland/FTP-Deploy-Action)
235 | ```
236 |
237 | ---
238 |
239 | ## FAQ
240 |
241 | How to exclude .git files from the publish
242 |
243 | Git files are excluded by default! If you customize the `exclude` option make sure you re-add the default options.
244 |
245 |
246 |
247 | How to exclude a specific file or folder
248 |
249 | You can use the `exclude` option to ignore specific files/folders from the publish. Keep in mind you will need to re-add the default exclude options if you want to keep them. For example the below option excludes all `.txt` files.
250 |
251 | ```yml
252 | exclude:
253 | - *.txt
254 | ```
255 |
256 |
257 |
258 |
259 |
260 | How do I set a upload timeout?
261 |
262 | github has a built-in `timeout-minutes` option, see customized example below
263 |
264 | ```yaml
265 | on: push
266 | name: Publish Website
267 | jobs:
268 | web-deploy:
269 | name: web-deploy
270 | runs-on: ubuntu-latest
271 | timeout-minutes: 15 # time out after 15 minutes (default is 360 minutes)
272 | steps:
273 | ....
274 | ```
275 |
276 |
277 | ## Debugging your config locally
278 | This action is a basic wrapper around my `@samkirkland/ftp-deploy` npm package. To test your config you can install [@samkirkland/ftp-deploy](https://github.com/SamKirkland/ftp-deploy) and then convert your config to a yml action. Settings are one-to-one, this action is only a wrapper.
279 |
280 | ## Contributing to this project
281 | To test this action locally you will need to setup **docker** and **act** to run a environment similar to the one github uses for actions.
282 | - Download/install docker for windows, make sure it is running
283 | - `choco install act-cli` install [act](https://github.com/nektos/act)
284 | - Install the npm package using `npm install --dev-only @samkirkland/ftp-deploy`
285 | - Update the `deploy` script in `package.json` with a actual server/username/password
286 | - You can run the script using the following command `npm run deploy` (run this in the folder that has the `package.json` file)
287 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: "FTP Deploy"
2 | description: "Automate deploying websites and more with this GitHub action via FTP and FTPS"
3 | author: "Sam Kirkland"
4 | inputs:
5 | server:
6 | required: true
7 | description: "ftp server"
8 | username:
9 | required: true
10 | description: "ftp username"
11 | password:
12 | required: true
13 | description: "ftp password"
14 | port:
15 | required: false
16 | description: "Server port to connect to (read your web hosts docs)"
17 | protocol:
18 | required: false
19 | description: "protocol to deploy with - ftp, ftps, or ftps-legacy"
20 | local-dir:
21 | required: false
22 | description: "Folder to upload from, must end with trailing slash /"
23 | server-dir:
24 | required: false
25 | description: "Path to upload to on the server. Must end with trailing slash /"
26 | state-name:
27 | required: false
28 | description: "Path and name of the state file - this file is used to track which files have been deployed"
29 | dry-run:
30 | required: false
31 | description: "Prints which modifications will be made with current config options, but doesnt actually make any changes"
32 | dangerous-clean-slate:
33 | required: false
34 | description: "Deletes ALL contents of server-dir, even items in excluded with exclude argument"
35 | exclude:
36 | required: false
37 | description: "An array of glob patterns, these files will not be included in the publish/delete process"
38 | log-level:
39 | required: false
40 | description: "How verbose should the information be - minimal, standard, or verbose"
41 | security:
42 | required: false
43 | description: "strict or loose"
44 | timeout:
45 | required: false
46 | description: "Timeout in milliseconds for FTP operations"
47 | runs:
48 | using: "node20"
49 | main: "dist/index.js"
50 | branding:
51 | icon: "upload-cloud"
52 | color: "blue"
53 |
--------------------------------------------------------------------------------
/images/ftp-deploy-logo-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamKirkland/FTP-Deploy-Action/8e83cea8672e3fbcbb9fdafff34debf6ae4c5f65/images/ftp-deploy-logo-full.png
--------------------------------------------------------------------------------
/images/ftp-deploy-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SamKirkland/FTP-Deploy-Action/8e83cea8672e3fbcbb9fdafff34debf6ae4c5f65/images/ftp-deploy-logo-small.png
--------------------------------------------------------------------------------
/migration.md:
--------------------------------------------------------------------------------
1 | # How to migrate between versions
2 |
3 | ## Migrating from v4.1.0 to v4.2.0
4 |
5 | `v4.2.0` parses the `exclude` option in a more standard way. Going forward the `exclude` option **must** be in the following format:
6 |
7 | ```yml
8 | exclude: |
9 | **/.git*
10 | **/.git*/**
11 | **/node_modules/**
12 | fileToExclude.txt
13 | ```
14 |
15 | ## Migrating from v3 to v4
16 |
17 | Migrating from v3 to v4 should be fairly straightforward. Version 4 was designed with speed and ease of initial setup in mind. Going forward version 4 will be the only supported version.
18 |
19 | ### Those who can't upgrade
20 |
21 | Most features have been carried forward and improved upon. However, some features did not make the cut:
22 | - **`sftp` is no longer supported**. If you have `sftp` access you are using `ssh`, that means you have access to a much more modern and capable protocol. I plan on releasing a separate github action that will deploy over `sftp`/`ssh` using `rsync`. Until then you can continue using version 3.
23 | - The `include` argument has been removed. I didn't see much need for it in the initial release. If you need this feature please create a support ticket.
24 |
25 | ### How to upgrade
26 |
27 | 1. Remove `with: fetch-depth: 2`. It is no longer needed and removing it will _slightly_ speed up deployments.
28 | 2. Change the version to `v4.X.X`, for example `SamKirkland/FTP-Deploy-Action@v4.3.5` (please check the [README](https://github.com/SamKirkland/FTP-Deploy-Action/blob/master/README.md) or the [releases page](https://github.com/SamKirkland/FTP-Deploy-Action/releases/latest) for the latest version).
29 | 3. If you have a `.git-ftp-include` file you should delete it. Version 4 tracks files differently and no longer needs this config file.
30 | 4. If you have a `.git-ftp-ignore` file, you should transfer the options to the new `exclude` argument. **Note:** version 4 excludes any `.git*` and `node_modules/` files / folders by default.
31 | 5. Update your arguments to reflect the following changes:
32 | - `ftp-server` was split into 4 arguments:
33 | - `server`
34 | - `port`
35 | - `protocol`
36 | - `server-dir`
37 | - `ftp-username` was renamed to `username`.
38 | - `ftp-password` was renamed to `password`.
39 | - `local-dir` and `server-dir` now **must** end with `/`.
40 | - `git-ftp-args` and `known-hosts` arguments were removed.
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ftp-deploy-action",
3 | "version": "4.3.5",
4 | "private": true,
5 | "description": "Automate deploying websites and more with this GitHub action",
6 | "main": "dist/index.js",
7 | "scripts": {
8 | "build": "ncc build src/main.ts --no-cache",
9 | "test": "jest",
10 | "lint": "eslint src/**/*.ts"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/SamKirkland/FTP-Deploy-Action.git"
15 | },
16 | "keywords": [
17 | "ftp",
18 | "website deploy",
19 | "continuous integration",
20 | "ftps"
21 | ],
22 | "author": "Sam Kirkland",
23 | "license": "MIT",
24 | "dependencies": {
25 | "@actions/core": "^1.9.1",
26 | "@samkirkland/ftp-deploy": "^1.2.4",
27 | "@types/jest": "^29.4.1",
28 | "jest": "^29.5.0",
29 | "ts-jest": "^29.0.5",
30 | "ts-node-dev": "^2.0.0"
31 | },
32 | "devDependencies": {
33 | "@types/node": "^20.11.24",
34 | "@typescript-eslint/eslint-plugin": "^5.33.1",
35 | "@typescript-eslint/parser": "^5.33.1",
36 | "@vercel/ncc": "^0.34.0",
37 | "eslint": "^8.22.0",
38 | "eslint-plugin-jest": "^26.8.7",
39 | "typescript": "^4.7.4"
40 | },
41 | "jest": {
42 | "preset": "ts-jest"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/project.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {
8 | "files.exclude": {
9 | "**/node_modules": true
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/main.test.ts:
--------------------------------------------------------------------------------
1 | import { optionalBoolean, optionalInt, optionalLogLevel, optionalProtocol, optionalSecurity, optionalString } from "./parse";
2 |
3 | describe("boolean", () => {
4 | test("false", () => {
5 | expect(optionalBoolean("test", "false")).toBe(false);
6 | });
7 |
8 | test("FALSE", () => {
9 | expect(optionalBoolean("test", "FALSE")).toBe(false);
10 | });
11 |
12 | test("true", () => {
13 | expect(optionalBoolean("test", "true")).toBe(true);
14 | });
15 |
16 | test("TRUE", () => {
17 | expect(optionalBoolean("test", "TRUE")).toBe(true);
18 | });
19 |
20 | test("optional", () => {
21 | expect(optionalBoolean("test", "")).toBe(undefined);
22 | });
23 | });
24 |
25 | describe("string", () => {
26 | test("empty", () => {
27 | expect(optionalString("")).toBe(undefined);
28 | });
29 |
30 | test("populated", () => {
31 | expect(optionalString("test")).toBe("test");
32 | });
33 | });
34 |
35 | describe("int", () => {
36 | test("empty", () => {
37 | expect(optionalInt("test", "")).toBe(undefined);
38 | });
39 |
40 | test("0", () => {
41 | expect(optionalInt("test", "0")).toBe(0);
42 | });
43 |
44 | test("1", () => {
45 | expect(optionalInt("test", "1")).toBe(1);
46 | });
47 |
48 | test("500", () => {
49 | expect(optionalInt("test", "500")).toBe(500);
50 | });
51 |
52 | test("non-int", () => {
53 | expect(() => optionalInt("test", "12.345")).toThrow();
54 | });
55 | });
56 |
57 | describe("protocol", () => {
58 | test("empty", () => {
59 | expect(optionalProtocol("test", "")).toBe(undefined);
60 | });
61 |
62 | test("ftp", () => {
63 | expect(optionalProtocol("test", "ftp")).toBe("ftp");
64 | });
65 |
66 | test("ftps", () => {
67 | expect(optionalProtocol("test", "ftps")).toBe("ftps");
68 | });
69 |
70 | test("ftps-legacy", () => {
71 | expect(optionalProtocol("test", "ftps-legacy")).toBe("ftps-legacy");
72 | });
73 | });
74 |
75 | describe("log level", () => {
76 | test("empty", () => {
77 | expect(optionalLogLevel("test", "")).toBe(undefined);
78 | });
79 |
80 | test("minimal", () => {
81 | expect(optionalLogLevel("test", "minimal")).toBe("minimal");
82 | });
83 |
84 | test("standard", () => {
85 | expect(optionalLogLevel("test", "standard")).toBe("standard");
86 | });
87 |
88 | test("verbose", () => {
89 | expect(optionalLogLevel("test", "verbose")).toBe("verbose");
90 | });
91 | });
92 |
93 | describe("security", () => {
94 | test("empty", () => {
95 | expect(optionalSecurity("test", "")).toBe(undefined);
96 | });
97 |
98 | test("loose", () => {
99 | expect(optionalSecurity("test", "loose")).toBe("loose");
100 | });
101 |
102 | test("strict", () => {
103 | expect(optionalSecurity("test", "strict")).toBe("strict");
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import * as core from "@actions/core";
2 | import { deploy } from "@samkirkland/ftp-deploy";
3 | import { IFtpDeployArguments } from "@samkirkland/ftp-deploy/dist/types";
4 | import { optionalInt, optionalProtocol, optionalString, optionalBoolean, optionalStringArray, optionalLogLevel, optionalSecurity } from "./parse";
5 |
6 | async function runDeployment() {
7 | try {
8 | const args: IFtpDeployArguments = {
9 | server: core.getInput("server", { required: true }),
10 | username: core.getInput("username", { required: true }),
11 | password: core.getInput("password", { required: true }),
12 | port: optionalInt("port", core.getInput("port")),
13 | protocol: optionalProtocol("protocol", core.getInput("protocol")),
14 | "local-dir": optionalString(core.getInput("local-dir")),
15 | "server-dir": optionalString(core.getInput("server-dir")),
16 | "state-name": optionalString(core.getInput("state-name")),
17 | "dry-run": optionalBoolean("dry-run", core.getInput("dry-run")),
18 | "dangerous-clean-slate": optionalBoolean("dangerous-clean-slate", core.getInput("dangerous-clean-slate")),
19 | "exclude": optionalStringArray("exclude", core.getMultilineInput("exclude")),
20 | "log-level": optionalLogLevel("log-level", core.getInput("log-level")),
21 | "security": optionalSecurity("security", core.getInput("security")),
22 | "timeout": optionalInt("timeout", core.getInput("timeout"))
23 | };
24 |
25 | await deploy(args);
26 | }
27 | catch (error: any) {
28 | core.setFailed(error);
29 | }
30 | }
31 |
32 | runDeployment();
33 |
--------------------------------------------------------------------------------
/src/parse.ts:
--------------------------------------------------------------------------------
1 | export function optionalString(rawValue: string): string | undefined {
2 | if (rawValue.length === 0) {
3 | return undefined;
4 | }
5 |
6 | return rawValue;
7 | }
8 |
9 | export function optionalBoolean(argumentName: string, rawValue: string): boolean | undefined {
10 | if (rawValue.length === 0) {
11 | return undefined;
12 | }
13 |
14 | const cleanValue = rawValue.toLowerCase();
15 | if (cleanValue === "true") {
16 | return true;
17 | }
18 | if (cleanValue === "false") {
19 | return false;
20 | }
21 |
22 | throw new Error(`${argumentName}: invalid parameter - please use a boolean, you provided "${rawValue}". Try true or false instead.`);
23 | }
24 |
25 | export function optionalProtocol(argumentName: string, rawValue: string): "ftp" | "ftps" | "ftps-legacy" | undefined {
26 | if (rawValue.length === 0) {
27 | return undefined;
28 | }
29 |
30 | const cleanValue = rawValue.toLowerCase();
31 | if (cleanValue === "ftp") {
32 | return "ftp";
33 | }
34 | if (cleanValue === "ftps") {
35 | return "ftps";
36 | }
37 | if (cleanValue === "ftps-legacy") {
38 | return "ftps-legacy";
39 | }
40 |
41 | throw new Error(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "ftp", "ftps", or "ftps-legacy" instead.`);
42 | }
43 |
44 | export function optionalLogLevel(argumentName: string, rawValue: string): "minimal" | "standard" | "verbose" | undefined {
45 | if (rawValue.length === 0) {
46 | return undefined;
47 | }
48 |
49 | const cleanValue = rawValue.toLowerCase();
50 | if (cleanValue === "minimal") {
51 | return "minimal";
52 | }
53 | if (cleanValue === "standard") {
54 | return "standard";
55 | }
56 | if (cleanValue === "verbose") {
57 | return "verbose";
58 | }
59 |
60 | throw new Error(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "minimal", "standard", or "verbose" instead.`);
61 | }
62 |
63 | export function optionalSecurity(argumentName: string, rawValue: string): "loose" | "strict" | undefined {
64 | if (rawValue.length === 0) {
65 | return undefined;
66 | }
67 |
68 | const cleanValue = rawValue.toLowerCase();
69 | if (cleanValue === "loose") {
70 | return "loose";
71 | }
72 | if (cleanValue === "strict") {
73 | return "strict";
74 | }
75 |
76 | throw new Error(`${argumentName}: invalid parameter - you provided "${rawValue}". Try "loose" or "strict" instead.`);
77 | }
78 |
79 | export function optionalInt(argumentName: string, rawValue: string): number | undefined {
80 | if (rawValue.length === 0) {
81 | return undefined;
82 | }
83 |
84 | const valueAsNumber = parseFloat(rawValue);
85 |
86 | if (Number.isInteger(valueAsNumber)) {
87 | return valueAsNumber;
88 | }
89 |
90 | throw new Error(`${argumentName}: invalid parameter - you provided "${rawValue}". Try a whole number (no decimals) instead like 1234`);
91 | }
92 |
93 | export function optionalStringArray(argumentName: string, rawValue: string[]): string[] | undefined {
94 | if (rawValue.length === 0) {
95 | return undefined;
96 | }
97 |
98 | if (typeof rawValue === "string") {
99 | throw new Error(`${argumentName}: invalid parameter - you provided "${rawValue}". This option expects an list in the EXACT format described in the readme`);
100 | }
101 |
102 | return rawValue;
103 | }
104 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "Node",
4 | "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | "outDir": "./dist", /* Redirect output structure to the directory. */
7 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
8 | "strict": true, /* Enable all strict type-checking options. */
9 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
10 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
11 | "noEmit": true
12 | },
13 | "exclude": [
14 | "node_modules",
15 | "**/.test.ts"
16 | ]
17 | }
--------------------------------------------------------------------------------