This commit is contained in:
SamKirkland 2020-08-26 01:28:40 -05:00
parent 1053c3278e
commit 3576f3af9c
23 changed files with 7764 additions and 5829 deletions

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
dist/
lib/
node_modules/

56
.eslintrc.json Normal file
View File

@ -0,0 +1,56 @@
{
"plugins": [
"jest",
"@typescript-eslint"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/func-call-spacing": [
"error",
"never"
],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/type-annotation-spacing": "error"
},
"env": {
"node": true,
"es6": true,
"jest/globals": true
}
}

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

23
.github/workflows/ftp.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: FTP Test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
deploy: # make sure the action works on a clean machine without building
name: 🚀 Deploy website every commit
runs-on: ubuntu-latest
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v2
- name: 📂 Sync files
uses: ./
with:
server: ftp.samkirkland.com
username: test@samkirkland.com
password: ${{ secrets.ftp_password }}

24
.github/workflows/ftps.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: FTPS Test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
deploy: # make sure the action works on a clean machine without building
name: 🚀 Deploy website every commit
runs-on: ubuntu-latest
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v2
- name: 📂 Sync files
uses: ./
with:
server: wwwssr16.supercp.com
username: test@samkirkland.com
password: ${{ secrets.ftp_password }}
protocol: ftps
secure: strict

View File

@ -1,16 +0,0 @@
on: push
name: Test FTP Deploy
jobs:
FTP-Deploy-Action:
name: FTP-Deploy-Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@master
with:
ftp-server: ftp://ftp.samkirkland.com/
ftp-username: ${{ secrets.ftp_username }}
ftp-password: ${{ secrets.ftp_password }}

View File

@ -1,18 +0,0 @@
on: push
name: Test SFTP Deploy
jobs:
FTP-Deploy-Action:
name: FTP-Deploy-Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@master
with:
# deploy to a folder named "sftp-deploy-test.samkirkland.com" on my server
ftp-server: sftp://ftp.samkirkland.com:7822/home/samkirkland/sftp-deploy-test.samkirkland.com/
ftp-username: ${{ secrets.sftp_username }}
ftp-password: ${{ secrets.sftp_password }}
git-ftp-args: --insecure

13
.gitignore vendored
View File

@ -1,7 +1,5 @@
__tests__/runner/* # Dependency directory
node_modules
# comment out in distribution branches
node_modules/
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs # Logs
@ -91,3 +89,10 @@ typings/
# DynamoDB Local files # DynamoDB Local files
.dynamodb/ .dynamodb/
# OS metadata
.DS_Store
Thumbs.db
# Ignore built ts files
lib/**/*

View File

@ -1,5 +0,0 @@
{
"files.exclude": {
"**/node_modules": true
}
}

View File

@ -1,14 +0,0 @@
FROM debian:stable-slim
LABEL repository="https://github.com/SamKirkland/FTP-Deploy-Action"
LABEL maintainer="Sam Kirkland <FTP-Deploy-Action@samkirkland.com>"
RUN apt-get update
RUN apt-get install -y git
RUN apt-get install -y git-ftp
RUN apt-get install -y nodejs
COPY dist/index.js /deploy.js
RUN chmod +x deploy.js
ENTRYPOINT ["node", "../../deploy.js"]

455
README.md
View File

@ -1,338 +1,155 @@
<p align="center"> <p align="center">
<img alt="FTP Deploy - Continuous integration for everyone" src="images/ftp-upload-logo-small.png"> <img alt="FTP Deploy Action - Continuous integration for everyone" src="images/ftp-deploy-logo-small.png">
</p> </p>
> :warning: **This is a beta branch, use at your own risk**
Automate deploying websites and more with this GitHub action Automate deploying websites and more with this GitHub action
![Test FTP Deploy](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/Test%20FTP%20Deploy/badge.svg) ![Test SFTP Deploy](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/Test%20SFTP%20Deploy/badge.svg) ![FTP test](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/FTP%20Test/badge.svg)
![FTPS test](https://github.com/SamKirkland/FTP-Deploy-Action/workflows/FTPS%20Test/badge.svg)
--- ---
### Usage Example ### Usage Example
Place the following in `/.github/workflows/main.yml`
Place the following in `Your_Project/.github/workflows/main.yml`
```yml ```yml
on: push on: push
name: Publish Website name: Publish Website
jobs: jobs:
FTP-Deploy-Action: web-deploy:
name: FTP-Deploy-Action name: 🚀 Deploy website every commit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.1.0 - name: 🚚 Get latest code
uses: actions/checkout@v2.3.2
- name: 📂 Sync files
uses: SamKirkland/FTP-Deploy-Action@beta-4
with: with:
fetch-depth: 2 server: ftp.samkirkland.com
- name: FTP-Deploy-Action username: myFtpUserName
uses: SamKirkland/FTP-Deploy-Action@3.1.1 password: ${{ secrets.ftp_password }}
with:
ftp-server: ftp://ftp.samkirkland.com/
ftp-username: myFtpUserName
ftp-password: ${{ secrets.FTP_PASSWORD }}
``` ```
--- ---
### Requirements
- You must have ftp access to your server. If your host requires ssh please use my web-deploy action
- Some web hosts change the default port (21), check with your host for your port number
---
### Setup Steps ### Setup Steps
1. Select the repository you want to add the action to 1. Select the repository you want to add the action to
2. Select the `Actions` tab 2. Select the `Actions` tab
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` 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`
4. Paste the example above into your yaml file and save 4. Paste the example above into your yaml file and save
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 `ftp-password` 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`
6. Update your yaml file settings 6. Update your yaml file settings
__Note: Only tracked files will be published by default. If you want to publish files that don't exist in github (example: files generated during the action run) you must add those files/folders to `.git-ftp-include`__
Migrating from v2? See the [migration guide](v2-v3-migration.md)
--- ---
### Settings ### Settings
Keys can be added directly to your .yml config file or referenced from your project `Secrets` storage. Keys can be added directly to your .yml config file or referenced from your project `Secrets` storage.
To add a `secret` go to the `Settings` tab in your project then select `Secrets`. To add a `secret` go to the `Settings` tab in your project then select `Secrets`.
I recommend you store your `ftp-password` as a secret. I strongly recommend you store your `password` as a secret.
| Key Name | Required? | Example | Default | Description | | Key Name | Required | Example | Default Value | Description |
|----------------|-----------|-----------------------------------------------|---------|----------------------------------------------------------| |-------------------------|----------|----------------------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ftp-server` | Yes | ftp://ftp.samkirkland.com/destinationPath/ | | Deployment destination server & path. Formatted as `protocol://domain.com:port/destinationPath/` protocol can be `ftp`, `ftps`, or `sftp`. Port is optional, when not specified it will default to 21 when using ftp, 22 when using sftp, and 990 when using ftps | | `server` | Yes | `ftp.samkirkland.com` | | Deployment destination server |
| `ftp-username` | Yes | username@samkirkland.com | | FTP account username | | `username` | Yes | `username@samkirkland.com` | | FTP user name |
| `ftp-password` | Yes | CrazyUniquePassword&%123 | | FTP account password | | `password` | Yes | `CrazyUniquePassword&%123` | | FTP password, be sure to escape quotes and spaces |
| `local-dir` | No | deploy/ | ./ | Which local folder to deploy, path should be relative to the root and should include trailing slash. `./` is the root of the project | | `port` | No | `990` | `21` | Server port to connect to (read your web hosts docs) |
| `git-ftp-args` | No | See `git-ftp-args` section below | | Custom git-ftp arguments, this field is passed through directly into the git-ftp script | | `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) |
| `known-hosts` | No | hostname ssh-rsa AAAAB3NzaC1y ... | | The desired contents of your .ssh/known_hosts file. See [known hosts setup](#known-hosts-setup) | | `local-dir` | No | `./myFolderToPublish/` | `./` | Path to upload to on the server, must end with trailing slash `/` |
| `server-dir` | No | `public_html/www/` | `./` | Folder to upload from, must end with trailing slash `/` |
| `state-name` | No | `folder/.sync-state.json` | `.ftp-deploy-sync-state.json` | Custom |
| `dry-run` | No | `true` | `false` | :warning: todo - Prints which modifications will be made with current config options, but doesn't actually make any changes |
| `dangerous-clean-slate` | No | `true` | `false` | :warning: todo - Deletes ALL contents of server-dir, even items in excluded with 'exclude' argument |
| `include` | No | | `` | :warning: todo - An array of glob patterns, these files will always be included in the publish/delete process - even if no change occurred |
| `exclude` | No | | `.git*` `.git*/**` `node_modules/**` `node_modules/**/*` | :warning: todo - An array of glob patterns, these files will not be included in the publish/delete process |
| `log-level` | No | `info` | `info` | `warn`: only important/warning info, `info`: default, log important/warning info & progress info, `debug`: log everything for debugging |
#### Advanced options using `git-ftp-args`
Custom arguments, this field is passed through directly into the git-ftp script. See [git-ftp's manual](https://github.com/git-ftp/git-ftp/blob/master/man/git-ftp.1.md) for all options.
You can use as many arguments as you want, seperate them with a space
Below is an incomplete list of commonly used args:
| Argument | Description |
|------------------------|------------------------------------------------------------------------------------------------------|
| `--dry-run` | Does not upload or delete anything, but tries to get the .git-ftp.log file from remote host |
| `--silent` | Be silent |
| `--all` | Transfer all files, even seemingly the same as the target site (default is differences only). Note: Only files committed to github are uploaded, if you'd like to upload files generated during the action run see `.git-ftp-include` |
| `--lock` | Locks remote files from being modified while a deployment is running |
| `--remote-root` | Specifies the remote root directory to deploy to. The remote path in the URL is ignored |
| `--key` | SSH private key file name for SFTP |
| `--branch` | Push a specific branch. I recommend [creating a yaml action for each branch instead](https://github.com/SamKirkland/FTP-Deploy-Action/issues/37#issuecomment-579819486) |
| `--pubkey` | SSH public key file name. Used with `--key` option |
| `--insecure` | Don't verify server's certificate |
| `--cacert <file>` | Use as CA certificate store. Useful when a server has a self-signed certificate |
### Ignore specific files when deploying
Add patterns to `.git-ftp-ignore` and all matching file names will be ignored. The patterns are interpreted as shell glob patterns.
Here are some glob pattern examples:
#### Ignore git related files:
```gitattributes
.gitignore
*/.gitignore # ignore files in sub directories
*/.gitkeep
.git-ftp-ignore
.git-ftp-include
.gitlab-ci.yml
```
#### Ignore a single file called `foobar.txt`
```gitattributes
foobar.txt
```
#### Ignore all files having extension .txt
```gitattributes
*.txt
```
#### Ignore everything in a directory named `config`
```gitattributes
config/*
```
### Force upload specific files
The `.git-ftp-include` file specifies intentionally untracked files to should upload. If you have a file that should always be uploaded, add a line beginning with `!` followed by the file's name.
#### Always upload the file `VERSION.txt`
```gitattributes
!VERSION.txt
```
#### Always upload the folder `build`
```gitattributes
!build/
```
If you have a file that should be uploaded whenever a tracked file changes, add a line beginning with the untracked file's name followed by a colon and the tracked file's name.
#### Upload CSS file compiled from an SCSS file
```gitattributes
css/style.css:scss/style.scss
```
If you have multiple source files, you can add multiple lines for each of them. Whenever one of the tracked files changes, the upload of the paired untracked file will be triggered.
```gitattributes
css/style.css:scss/style.scss
css/style.css:scss/mixins.scss
```
If a local untracked file is deleted, any change of a paired tracked file will trigger the deletion of the remote file on the server.
All paths are usually relative to the Git working directory. When using the `local-dir` option, paths of tracked files (right side of the colon) are relative to the set `local-dir`.
```gitattributes
# upload "html/style.css" triggered by html/style.scss
# with local-dir "html"
html/style.css:style.scss
```
If your source file is outside the `local-dir`, prefix it with a / and define a path relative to the Git working directory.
#### Uploading a file outside of `local-dir`
```gitattributes
# upload "dist/style.css" with local-dir "dist"
dist/style.css:/src/style.scss
```
It is also possible to upload whole directories. For example, if you use a package manager like composer, you can upload all vendor packages when the file composer.lock changes:
```gitattributes
vendor/:composer.lock
```
But keep in mind that this will upload all files in the vendor folder, even those that are on the server already. And it will not delete files from that directory if local files are deleted.
---
# Common Examples # Common Examples
#### Build and Publish React/Angular/Vue Website
Read more about the differences between these protocols [https://www.sftp.net/sftp-vs-ftps](https://www.sftp.net/sftp-vs-ftps) Make sure you have an npm script named 'build'. This config should work for most node built websites.
### FTP (File Transfer Protocol)
`ftp://ftp.samkirkland.com:21/mypath`
FTP has existed since 1971, it's an ancient protocol with near universal support.
```yml ```yml
on: push on: push
name: Publish Website name: Publish Website
jobs: jobs:
FTP-Deploy-Action: web-deploy:
name: FTP-Deploy-Action name: 🚀 Deploy website every commit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.1.0 - name: 🚚 Get latest code
uses: actions/checkout@v2.3.2
- name: Use Node.js 12
uses: actions/setup-node@v2-beta
with: with:
fetch-depth: 2 node-version: '12'
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@3.1.1
with:
ftp-server: ftp://ftp.samkirkland.com/
ftp-username: myFtpUserName
ftp-password: ${{ secrets.FTP_PASSWORD }}
```
### FTPS (File Transfer Protocol over SSL) - name: 🔨 Build Project
`ftps://ftp.samkirkland.com:21/mypath`
Use the legacy FTP over a secure encrypted connection.
Notes about ftps:
- Most hosts don't offer FTPS, it's more common on windows/.net hosts and less common on linux hosting
- Most hosts don't have a correct certificate setup for ftp domains, [even my host doesn't do it right](https://ftp.samkirkland.com/). This means you'll likely have to add `--insecure` to `git-ftp-args`
```yml
on: push
name: Publish Website over FTPS
jobs:
FTP-Deploy-Action:
name: FTP-Deploy-Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@3.1.1
with:
ftp-server: ftps://ftp.samkirkland.com:21/
ftp-username: myFTPSUsername
ftp-password: ${{ secrets.FTPS_PASSWORD }}
git-ftp-args: --insecure # if your certificate is setup correctly this can be removed
```
### SFTP (SSH File Transfer Protocol)
`sftp://ftp.samkirkland.com:22/mypath`
Similar in name to FTP but this protocol is entirely new and requires SSH access to the server.
##### Notes about SFTP:
- **You CANNOT use a FTP account - they are not the same!**
- You must have shell access to your server, please read you webhosts documentation
- You will need to create a **SSH** user to deploy over SFTP. Normally this is your cpanel or hosting providers username and password
- Most web hosts change the default port (21), check with your host for your port number
### Known Hosts Setup
**Windows**
In powershell run `ssh-keyscan -p <sshport> <hostname>` and copy the hash output
Example for samkirkland.com `ssh-keyscan -p 7822 samkirkland.com`
**Linux, or OSX (using homebrew)**
Install the OpenSSH packages and use `ssh-keyscan <hostname>` and copy the hash output
On macOS you can use `ssh-keyscan <hostname> | pbcopy` to copy the hash output to your clipboard
Add the `known-hosts` argument with your hosts hash
Example: `knownhosts: ssh-rsa AAAAB3Nza...H1Q5Spw==`
*Note: If you receive a `Connection refused` error, you must specify the ssh port to your host*
```yml
on: push
name: Publish Website over SFTP
jobs:
FTP-Deploy-Action:
name: FTP-Deploy-Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@3.1.1
with:
ftp-server: sftp://ftp.samkirkland.com:7280/
ftp-username: mySFTPUsername
ftp-password: ${{ secrets.SFTP_PASSWORD }}
known-hosts: [samkirkland.com]:7822 ssh-rsa AAAA...5Spw==
# add the following line instead if your certificate is setup incorrectly
# git-ftp-args: --insecure
```
### Build and Publish React/Angular/Vue Website
Make sure you have an npm script named 'build'. This config should work for most node built websites.
> #### If you don't commit your `build` folder to github you MUST create a `.git-ftp-include` file with the content `!build/` so the folder is always uploaded!
```yml
on: push
name: Build and Publish Front End Framework Website
jobs:
FTP-Deploy-Action:
name: FTP-Deploy-Action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Build Project
run: | run: |
npm install npm install
npm run build --if-present npm run build
- name: List output files - name: 📂 Sync files
run: ls uses: SamKirkland/FTP-Deploy-Action@beta-4
- name: FTP-Deploy-Action
uses: SamKirkland/FTP-Deploy-Action@3.1.1
with: with:
ftp-server: ftp://ftp.samkirkland.com/ server: ftp.samkirkland.com
ftp-username: myFTPUsername username: myFtpUserName
ftp-password: ${{ secrets.FTP_PASSWORD }} password: ${{ secrets.password }}
local-dir: build # This folder is NOT going to upload by default unless you add it to .git-ftp-include
``` ```
### Log only dry run: Use this mode for testing #### FTPS
```yml
on: push
name: Publish Website Dry Run
jobs:
web-deploy:
name: 🚀 Deploy website every commit
runs-on: ubuntu-latest
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v2.3.2
- name: 📂 Sync files
uses: SamKirkland/FTP-Deploy-Action@beta-4
with:
server: ftp.samkirkland.com
username: myFtpUserName
password: ${{ secrets.password }}
protocol: ftps
```
#### Log only dry run: Use this mode for testing
Ouputs a list of files that will be created/modified to sync your source without making any actual changes Ouputs a list of files that will be created/modified to sync your source without making any actual changes
```yml ```yml
on: push on: push
name: Publish Website Dry Run name: Publish Website Dry Run
jobs: jobs:
FTP-Deploy-Action: web-deploy:
name: FTP-Deploy-Action name: 🚀 Deploy website every commit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.1.0 - name: 🚚 Get latest code
with: uses: actions/checkout@v2.3.2
fetch-depth: 2
- name: FTP-Deploy-Action - name: 📂 Sync files
uses: SamKirkland/FTP-Deploy-Action@3.1.1 uses: SamKirkland/FTP-Deploy-Action@beta-4
with: with:
ftp-server: ftp://ftp.samkirkland.com/ server: ftp.samkirkland.com
ftp-username: myFTPUsername username: myFtpUserName
ftp-password: ${{ secrets.FTP_PASSWORD }} password: ${{ secrets.password }}
git-ftp-args: --dry-run dry-run: true
``` ```
_Want another example? Let me know by creating a github issue_ _Want another example? Let me know by creating a [github issue](https://github.com/SamKirkland/FTP-Deploy-Action/issues/new)_
--- ---
@ -340,21 +157,16 @@ _Want another example? Let me know by creating a github issue_
<details> <details>
<summary>How to exclude .git files from the publish</summary> <summary>How to exclude .git files from the publish</summary>
See the [`.git-ftp-ignore`](#ignore-specific-files-when-deploying) example section todo
</details> </details>
<details> <details>
<summary>All files are being uploaded instead of just different files</summary> <summary>How to exclude a specific file or folder</summary>
By default only different files are uploaded. todo
Verify you have `with: fetch-depth: 2` in your `actions/checkout@v2.1.0` step. The last 2 checkins are required in order to determine differences
If you've had multiple git commits without deploying, all files will be uploaded to get back in sync
Verify you **don't** have the `--all` git-ftp-args flag set
</details> </details>
<details> <details>
<summary>How do I set a upload timeout?</summary> <summary>How do I set a upload timeout?</summary>
@ -364,8 +176,8 @@ github has a built-in `timeout-minutes` option, see customized example below
on: push on: push
name: Publish Website name: Publish Website
jobs: jobs:
FTP-Deploy-Action: web-deploy:
name: FTP-Deploy-Action name: web-deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 # time out after 15 minutes (default is 360 minutes) timeout-minutes: 15 # time out after 15 minutes (default is 360 minutes)
steps: steps:
@ -373,72 +185,17 @@ jobs:
``` ```
</details> </details>
---
## Common Errors
<details id="failed-to-upload">
<summary>Failed to upload files</summary>
* **Fix 1:** Verify your login credentials are correct, download a ftp client and test with the exact same host/username/password
* **Fix 2:** Remember if you are using SFTP or FTPS you cannot use a normal FTP account username/password. You must use a elevated account. Each host has a different process to setup a FTPS or SFTP account. Please contact your host for help.
* **Fix 3:** If you are using sftp or ftps you should add `git-ftp-args: --insecure`, most hosts setup certificates incorrectly :(
</details>
<details id="cant-access-remote-sftp">
<summary>Can't access remote 'sftp://', exiting...</summary>
See ["Failed to upload files"](#failed-to-upload) section above
</details>
<details id="cant-access-remote-ftps">
<summary>Can't access remote 'ftps://', exiting...</summary>
See ["Failed to upload files"](#failed-to-upload) section above
</details>
<details id="files-arent_uploading">
<summary>My files aren't uploading</summary>
V3+ uses github to determine when files have changes and only publish differences. This means files that aren't committed to github will not upload by default.
To change this behavior please see [`.git-ftp-ignore`](#ignore-specific-files-when-deploying) documentation.
</details>
<details id="prohibited-file-name">
<summary>rm: Access failed: 553 Prohibited file name: ./.ftpquota</summary>
The `.ftpquota` file is created by some FTP Servers and cannot be modified by the user
Add `.ftpquota` to your [`.git-ftp-ignore`](#ignore-specific-files-when-deploying) file
</details>
<details id="ssl-peer-certificate">
<summary>Error: SSL peer certificate or SSH remote key was not OK</summary>
Whitelist your host via the `known-hosts` configuration option (see [known hosts setup](#known-hosts-setup) in SFTP) or add the `--insecure` argument
</details>
---
## Debugging locally ## Debugging locally
##### Instructions for debugging Windows - Install the npm package using `npm install --dev-only @samkirkland/ftp-deploy`
- [Install docker](https://docs.docker.com/get-docker/) - Add a new key to your `package.json` file under `scripts`
- Open powershell **as Administrator** - You can run the script using the following command `npm run deploy` (run this in the folder that has the `package.json` file)
- Install [act-cli](https://github.com/nektos/act#installation) by running `choco install act-cli`
- Navigate to the repo folder
- Run `npm install` - this will install all dependencies to build this project
- Run `npm build` - this will build the action javascript and watch/rebuild when files change
- Run `npm run build-docker` - this will build the docker container (only needs to be done once)
- Run `npm run run-docker` - this will spin up a local copy of the action defined in `/debug/local-debug-deployment.yaml`. Update package.json to set any secret values
#### Instructions for debugging on Linux Example of `package.json`:
- [Install docker](https://docs.docker.com/get-docker/) on a Debian-based distro you can run `sudo apt install docker docker.io` ```json
- Open the terminal {
- Install [act-cli](https://github.com/nektos/act#installation) "scripts": {
- Navigate to the repo folder "deploy": "ftp-deploy --server ftp.samkirkland.com --username test@samkirkland.com --password \"CrazyUniquePassword&%123\"",
- Run `npm install` - this will install all dependencies to build this project },
- Run `npm build` - this will build the action javascript and watch/rebuild when files change }
- Run `npm run build-docker` - this will build the docker container (only needs to be done once) ```
- Run `npm run run-docker` - this will spin up a local copy of the action defined in `/debug/local-debug-deployment.yaml`. Update package.json to set any secret values
#### Pull Requests Welcome!

View File

@ -1,30 +1,19 @@
name: 'FTP Deploy' name: 'FTP Deploy'
description: 'Syncs files via FTP/SFTP to a remote server' description: 'Automate deploying websites and more with this GitHub action'
author: 'Sam Kirkland' author: 'Sam Kirkland'
inputs: inputs:
ftp-server: server:
description: 'Deployment destination server & path. Formatted as protocol://domain.com:port/full/destination/path/'
required: true required: true
ftp-username: description: 'ftp server'
description: 'FTP account username' username:
required: true required: true
ftp-password: description: 'ftp username'
description: 'FTP account password' password:
required: true required: true
local-dir: description: 'ftp password'
description: 'The local folder to copy, defaults to root project folder'
default: ./
required: false
git-ftp-args:
description: 'Passes through options into git-ftp'
default:
required: false
known-hosts:
description: The desired content of your `.ssh/known_hosts` file
required: false
runs: runs:
using: 'docker' using: 'node12'
image: 'Dockerfile' main: 'dist/index.js'
branding: branding:
icon: 'upload-cloud' icon: 'upload-cloud'
color: 'orange' color: 'orange'

View File

@ -1,18 +0,0 @@
on: push
name: Local Debug Deployment
jobs:
Local-Debug-Deployment:
name: Local-Debug-Deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.1.0
with:
fetch-depth: 2
- name: FTP-Deploy-Action
uses: ./
with:
ftp-server: ftp://ftp.samkirkland.com/
ftp-username: ${{ secrets.username }}
ftp-password: ${{ secrets.password }}
git-ftp-args: --dry-run

7688
dist/index.js vendored

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

5276
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +1,37 @@
{ {
"name": "typescript-action", "name": "ftp-deploy-action",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"description": "TypeScript template action", "description": "Automate deploying websites and more with this GitHub action",
"main": "dist/main.js", "main": "dist/main.js",
"engines": {
"node": ">=12.0.0"
},
"scripts": { "scripts": {
"build": "ncc build src/main.ts -o dist --watch", "build": "ncc build src/main.ts",
"build-docker": "docker build --tag action .", "lint": "eslint src/**/*.ts",
"run-docker": "act --workflows ./debug/ --secret username=UserNameHere --secret password=PasswordHere" "all": "npm run build && npm run lint"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/actions/typescript-action.git" "url": "git+https://github.com/SamKirkland/FTP-Deploy-Action.git"
}, },
"keywords": [ "keywords": [
"actions", "website deploy",
"node", "continuous integration",
"setup" "ftp",
"ftps"
], ],
"author": "SamKirkland", "author": "Sam Kirkland",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "1.2.4", "@actions/core": "^1.2.4",
"@actions/exec": "1.0.4" "@samkirkland/ftp-deploy": "^0.0.1",
"ts-node-dev": "^1.0.0-pre.56"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "25.2.1", "@types/node": "^14.0.27",
"@types/node": "12.12.8", "@typescript-eslint/parser": "^3.8.0",
"@zeit/ncc": "0.22.1", "@zeit/ncc": "^0.22.3",
"jest": "25.5.3", "eslint": "^7.6.0",
"jest-circus": "25.5.3", "js-yaml": "^3.14.0",
"ts-jest": "25.4.0", "typescript": "^3.9.7"
"typescript": "3.8.3"
} }
} }

12
project.code-workspace Normal file
View File

@ -0,0 +1,12 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.exclude": {
"**/node_modules": true
}
}
}

View File

@ -1,91 +1,31 @@
import * as core from '@actions/core'; import * as core from "@actions/core";
import * as exec from '@actions/exec'; import { deploy } from "@samkirkland/ftp-deploy";
import fs from 'fs'; import { IFtpDeployArguments } from "@samkirkland/ftp-deploy/dist/module/types";
import { promisify } from 'util';
import { IActionArguments } from './types';
const writeFileAsync = promisify(fs.writeFile); async function runDeployment() {
const errorDeploying = "⚠️ Error deploying"; const args: IFtpDeployArguments = {
server: core.getInput("server", { required: true }),
async function run() { username: core.getInput("username", { required: true }),
try { password: core.getInput("password", { required: true }),
const userArguments = getUserArguments(); protocol: core.getInput("protocol") as any, // todo fix
port: core.getInput("port") as any, // todo fix
await configureHost(userArguments); "local-dir": core.getInput("local-dir") as any, // todo fix
await syncFiles(userArguments); "server-dir": core.getInput("server-dir") as any, // todo fix
"state-name": core.getInput("state-name") as any, // todo fix
console.log("✅ Deploy Complete"); "dry-run": core.getInput("dry-run") as any, // todo fix
} "dangerous-clean-slate": core.getInput("dangerous-clean-slate") as any, // todo fix
catch (error) { "include": core.getInput("include") as any, // todo fix
console.error(errorDeploying); "exclude": core.getInput("exclude") as any, // todo fix
core.setFailed(error.message); "log-level": core.getInput("log-level") as any // todo fix
}
}
run();
async function configureHost(args: IActionArguments): Promise<void> {
if (args.knownHosts === "") {
return;
}
try {
const sshFolder = `${process.env['HOME']}/.ssh`;
await exec.exec(`mkdir -v -p ${sshFolder}`);
await exec.exec(`chmod 700 ${sshFolder}`);
writeFileAsync(`${sshFolder}/known_hosts`, args.knownHosts);
await exec.exec(`chmod 755 ${sshFolder}/known_hosts`);
console.log("✅ Configured known_hosts");
}
catch (error) {
console.error("⚠️ Error configuring known_hosts");
core.setFailed(error.message);
}
}
function getUserArguments(): IActionArguments {
return {
ftp_server: core.getInput("ftp-server", { required: true }),
ftp_username: core.getInput("ftp-username", { required: true }),
ftp_password: core.getInput("ftp-password", { required: true }),
local_dir: withDefault(core.getInput("local-dir"), "./"),
gitFtpArgs: withDefault(core.getInput("git-ftp-args"), ""),
knownHosts: withDefault(core.getInput("known-hosts"), "")
}; };
}
function withDefault(value: string, defaultValue: string) {
if (value === "" || value === null || value === undefined) {
return defaultValue;
}
return value;
}
/**
* Sync changed files
*/
async function syncFiles(args: IActionArguments) {
try { try {
await core.group("Uploading files", async () => { await deploy(args);
return await exec.exec(
"git ftp push",
[
"--force",
"--auto-init",
"--verbose",
`--syncroot=${args.local_dir}`,
`--user=${args.ftp_username}`,
`--passwd=${args.ftp_password}`,
args.gitFtpArgs!,
args.ftp_server!
]
);
});
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error);
} }
} }
runDeployment();

View File

@ -1,31 +0,0 @@
export interface IActionArguments {
ftp_server: string | undefined;
ftp_username: string | undefined;
ftp_password: string | undefined;
/** @default "." */
local_dir: string | undefined;
/** @default "" */
gitFtpArgs: string | undefined;
/** @default "" */
knownHosts: string | undefined;
}
/**
* @see https://github.com/git-ftp/git-ftp/blob/master/man/git-ftp.1.md#exit-codes
*/
export enum gitFTPExitCode {
Successful = 0,
UnknownError = 1,
WrongUsage = 2,
MissingArguments = 3,
ErrorWhileUploading = 4,
ErrorWhileDownloading = 5,
UnknownProtocol = 6,
RemoteLocked = 7,
GitRelatedError = 8,
PreFTPPushHookFailed = 9,
LocalFileOperationFailed = 10
}

View File

@ -1,63 +1,14 @@
{ {
"compilerOptions": { "compilerOptions": {
/* Basic Options */ "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
// "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./lib", /* Redirect output structure to the directory. */ "outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */ "strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": [
"node_modules"
]
} }

View File

@ -1,33 +0,0 @@
# Migrating from v2 to v3
`uses: actions/checkout@v2.1.0` must now have the option `fetch-depth: 2`
Without the `fetch-depth` option diffs cannot be calculated and all files will be uploaded.
### Breaking changes
All arguments have been renamed to lower `kebab-case`
| Old Value | New Value | Notes |
|------------------|-----------------|----------------------------|
| `env:` | `with:` | ⚠ Before declaring settings in v2 you set a line to `env:` In v3+ that line must now read `with:` ⚠ |
| `FTP_SERVER` | `ftp-server` | |
| `FTP_USERNAME` | `ftp-username` | |
| `FTP_PASSWORD` | `ftp-password` | |
| `LOCAL_DIR` | `local-dir` | |
| `ARGS` | `git-ftp-args` | |
| `METHOD` | | `METHOD` has been removed. Instead specify the method within `ftp-server` (ex: ftp://server.com, ftps://server.com, sftp://sever.com) |
| `PORT` | | `PORT` has been removed. Instead specify the port between the domain and destination within `ftp-server` (ex: ftp://server.com:PORT/destination/) |
| `REMOTE_DIR` | | `REMOTE_DIR` has been removed. Instead specify the destination path within `ftp-server` (ex: ftp://server.com/full/destination/path/) |
### ARGS changes
| Old ARG | New ARG | Notes |
|---------------------|-----------------|----------------------------------------|
| `--include` | | use `.git-ftp-ignore` instead |
| `--include-glob` | | use `.git-ftp-ignore` instead |
| `--exclude` | | use `.git-ftp-ignore` instead |
| `--exclude-glob` | | use `.git-ftp-ignore` instead |
| `--delete-excluded` | | |
| `--no-empty-dirs` | | |
| `--parallel` | | |
| `--L` | | |
| `--ignore-time` | | v3 only uploads differences by default |