mirror of
https://github.com/SamKirkland/FTP-Deploy-Action.git
synced 2026-04-10 12:32:17 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd242eb2f0 | ||
|
|
b7930b6a2e | ||
|
|
f31cc90480 | ||
|
|
346a843f1e |
4
.github/workflows/test-ftp-deploy.yaml
vendored
4
.github/workflows/test-ftp-deploy.yaml
vendored
@@ -5,11 +5,11 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@master
|
uses: SamKirkland/FTP-Deploy-Action-Typescript@master
|
||||||
with:
|
with:
|
||||||
ftp-server: ftp://ftp.samkirkland.com/
|
ftp-server: ftp://ftp.samkirkland.com/
|
||||||
ftp-username: ${{ secrets.ftp_username }}
|
ftp-username: ${{ secrets.ftp_username }}
|
||||||
|
|||||||
4
.github/workflows/test-sftp-deploy.yaml
vendored
4
.github/workflows/test-sftp-deploy.yaml
vendored
@@ -5,11 +5,11 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@master
|
uses: SamKirkland/FTP-Deploy-Action-Typescript@master
|
||||||
with:
|
with:
|
||||||
# deploy to a folder named "sftp-deploy-test.samkirkland.com" on my server
|
# 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-server: sftp://ftp.samkirkland.com:7822/home/samkirkland/sftp-deploy-test.samkirkland.com/
|
||||||
|
|||||||
243
README.md
243
README.md
@@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
Automate deploying websites and more with this GitHub action
|
Automate deploying websites and more with this GitHub action
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
---
|
### Usage Example (Your_Project/.github/workflows/main.yml)
|
||||||
|
|
||||||
### Usage Example
|
|
||||||
|
|
||||||
Place the following in `Your_Project/.github/workflows/main.yml`
|
|
||||||
```yml
|
```yml
|
||||||
on: push
|
on: push
|
||||||
name: Publish Website
|
name: Publish Website
|
||||||
@@ -19,21 +17,18 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: ftp://ftp.samkirkland.com/
|
ftp-server: ftp://ftp.samkirkland.com/
|
||||||
ftp-username: myFtpUserName
|
ftp-username: myFtpUserName
|
||||||
ftp-password: ${{ secrets.FTP_PASSWORD }}
|
ftp-password: ${{ secrets.FTP_PASSWORD }}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
#### 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`
|
||||||
@@ -41,28 +36,21 @@ jobs:
|
|||||||
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 `ftp-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
|
||||||
|
**Migrating from v2? See the [migration guide](v2-v3-migration.md)**
|
||||||
|
|
||||||
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 recommend you store your `ftp-password` as a secret.
|
||||||
|
|
||||||
| Key Name | Required? | Example | Default | Description |
|
| Key Name | Required? | Example | Default | 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 |
|
| `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 |
|
||||||
| `ftp-username` | Yes | username@samkirkland.com | | FTP account username |
|
| `ftp-username` | Yes | username@samkirkland.com | | FTP account username |
|
||||||
| `ftp-password` | Yes | CrazyUniquePassword&%123 | | FTP account password |
|
| `ftp-password` | Yes | CrazyUniquePassword&%123 | | FTP account password |
|
||||||
| `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 |
|
| `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 |
|
||||||
| `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 |
|
| `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 |
|
||||||
| `known-hosts` | No | hostname ssh-rsa AAAAB3NzaC1y ... | | The desired contents of your .ssh/known_hosts file. See [known hosts setup](#known-hosts-setup) |
|
|
||||||
|
|
||||||
#### Advanced options using `git-ftp-args`
|
#### 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.
|
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.
|
||||||
@@ -70,18 +58,19 @@ You can use as many arguments as you want, seperate them with a space
|
|||||||
|
|
||||||
Below is an incomplete list of commonly used args:
|
Below is an incomplete list of commonly used args:
|
||||||
|
|
||||||
| Argument | Description |
|
| Argument | Description |
|
||||||
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------|------------------------------------------------------------------------------------------------------|
|
||||||
| `--dry-run` | Does not upload or delete anything, but tries to get the .git-ftp.log file from remote host |
|
| `--dry-run` | Does not upload or delete anything, but tries to get the .git-ftp.log file from remote host |
|
||||||
| `--silent` | Be silent |
|
| `--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` |
|
| `--all` | Transfer all files, even seemingly the same as the target site (default is differences only) |
|
||||||
| `--lock` | Locks remote files from being modified while a deployment is running |
|
| `--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 |
|
| `--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 |
|
| `--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) |
|
| `--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 |
|
| `--pubkey` | SSH public key file name. Used with `--key` option |
|
||||||
| `--insecure` | Don't verify server's certificate |
|
| `--insecure` | Don't verify server's certificate |
|
||||||
| `--cacert <file>` | Use as CA certificate store. Useful when a server has a self-signed certificate |
|
| `--cacert <file>` | Use as CA certificate store. Useful when a server has a self-signed certificate |
|
||||||
|
|
||||||
|
|
||||||
### Ignore specific files when deploying
|
### 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.
|
Add patterns to `.git-ftp-ignore` and all matching file names will be ignored. The patterns are interpreted as shell glob patterns.
|
||||||
@@ -160,10 +149,7 @@ 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.
|
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
|
|
||||||
|
|
||||||
Read more about the differences between these protocols [https://www.sftp.net/sftp-vs-ftps](https://www.sftp.net/sftp-vs-ftps)
|
Read more about the differences between these protocols [https://www.sftp.net/sftp-vs-ftps](https://www.sftp.net/sftp-vs-ftps)
|
||||||
|
|
||||||
### FTP (File Transfer Protocol)
|
### FTP (File Transfer Protocol)
|
||||||
@@ -179,11 +165,11 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: ftp://ftp.samkirkland.com/
|
ftp-server: ftp://ftp.samkirkland.com/
|
||||||
ftp-username: myFtpUserName
|
ftp-username: myFtpUserName
|
||||||
@@ -195,7 +181,7 @@ jobs:
|
|||||||
|
|
||||||
Use the legacy FTP over a secure encrypted connection.
|
Use the legacy FTP over a secure encrypted connection.
|
||||||
|
|
||||||
Notes about ftps:
|
Notes about sftp:
|
||||||
- Most hosts don't offer FTPS, it's more common on windows/.net hosts and less common on linux hosting
|
- 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`
|
- 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`
|
||||||
|
|
||||||
@@ -207,12 +193,12 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: ftps://ftp.samkirkland.com:21/
|
ftp-server: ftps://ftp.samkirkland.com:21/
|
||||||
ftp-username: myFTPSUsername
|
ftp-username: myFTPSUsername
|
||||||
@@ -231,21 +217,6 @@ Similar in name to FTP but this protocol is entirely new and requires SSH access
|
|||||||
- You will need to create a **SSH** user to deploy over SFTP. Normally this is your cpanel or hosting providers username and password
|
- 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
|
- 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
|
|
||||||
|
|
||||||
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
|
```yml
|
||||||
on: push
|
on: push
|
||||||
name: Publish Website over SFTP
|
name: Publish Website over SFTP
|
||||||
@@ -254,24 +225,23 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: sftp://ftp.samkirkland.com:7280/
|
ftp-server: sftp://ftp.samkirkland.com:7280/
|
||||||
ftp-username: mySFTPUsername
|
ftp-username: mySFTPUsername
|
||||||
ftp-password: ${{ secrets.SFTP_PASSWORD }}
|
ftp-password: ${{ secrets.SFTP_PASSWORD }}
|
||||||
git-ftp-args: --insecure # if your certificate is setup correctly this can be removed (see known-hosts argument)
|
git-ftp-args: --insecure # if your certificate is setup correctly this can be removed
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Build and Publish React/Angular/Vue Website
|
### 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.
|
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
|
||||||
> #### 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
|
```yml
|
||||||
on: push
|
on: push
|
||||||
name: Build and Publish Front End Framework Website
|
name: Build and Publish Front End Framework Website
|
||||||
@@ -280,14 +250,14 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: Use Node.js 16
|
- name: Use Node.js 12.x
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '12.x'
|
||||||
|
|
||||||
- name: Build Project
|
- name: Build Project
|
||||||
run: |
|
run: |
|
||||||
@@ -298,12 +268,12 @@ jobs:
|
|||||||
run: ls
|
run: ls
|
||||||
|
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: ftp://ftp.samkirkland.com/
|
ftp-server: ftp://ftp.samkirkland.com/
|
||||||
ftp-username: myFTPUsername
|
ftp-username: myFTPUsername
|
||||||
ftp-password: ${{ secrets.FTP_PASSWORD }}
|
ftp-password: ${{ secrets.FTP_PASSWORD }}
|
||||||
local-dir: build # This folder is NOT going to upload by default unless you add it to .git-ftp-include
|
local-dir: build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Log only dry run: Use this mode for testing
|
### Log only dry run: Use this mode for testing
|
||||||
@@ -316,12 +286,12 @@ jobs:
|
|||||||
name: FTP-Deploy-Action
|
name: FTP-Deploy-Action
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.1.0
|
- uses: actions/checkout@master
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: FTP-Deploy-Action
|
- name: FTP-Deploy-Action
|
||||||
uses: SamKirkland/FTP-Deploy-Action@3.1.2
|
uses: SamKirkland/FTP-Deploy-Action@3.0.0
|
||||||
with:
|
with:
|
||||||
ftp-server: ftp://ftp.samkirkland.com/
|
ftp-server: ftp://ftp.samkirkland.com/
|
||||||
ftp-username: myFTPUsername
|
ftp-username: myFTPUsername
|
||||||
@@ -329,113 +299,36 @@ jobs:
|
|||||||
git-ftp-args: --dry-run
|
git-ftp-args: --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
_Want another example? Let me know by creating a github issue_
|
##### Want another example? Let me know by creating a github issue
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
<details>
|
1. `Can't access remote 'sftp://', exiting...` or `Can't access remote 'ftps://', exiting...`
|
||||||
<summary>How to exclude .git files from the publish</summary>
|
|
||||||
|
|
||||||
See the [`.git-ftp-ignore`](#ignore-specific-files-when-deploying) example section
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>All files are being uploaded instead of just different files</summary>
|
|
||||||
|
|
||||||
By default only different files are uploaded.
|
|
||||||
|
|
||||||
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>
|
|
||||||
<summary>How do I set a upload timeout?</summary>
|
|
||||||
|
|
||||||
github has a built-in `timeout-minutes` option, see customized example below
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
on: push
|
|
||||||
name: Publish Website
|
|
||||||
jobs:
|
|
||||||
FTP-Deploy-Action:
|
|
||||||
name: FTP-Deploy-Action
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 15 # time out after 15 minutes (default is 360 minutes)
|
|
||||||
steps:
|
|
||||||
....
|
|
||||||
```
|
|
||||||
</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 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 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 :(
|
* **Fix 3:** If you are using sftp or ftps you should add `git-ftp-args: --insecure`, most hosts setup certificates incorrectly :(
|
||||||
</details>
|
2. `rm: Access failed: 553 Prohibited file name: ./.ftpquota`
|
||||||
|
* **What is happening?** The `.ftpquota` file is created by some FTP Servers and cannot be modified by the user
|
||||||
|
* **Fix:** Add `.ftpquota` to your `.git-ftp-ignore` file
|
||||||
|
3. How to exclude .git files from the publish
|
||||||
|
* **Fix:** See the `.git-ftp-ignore` example above
|
||||||
|
4. All files are being uploaded instead of just different files
|
||||||
|
* By default only different files are uploaded.
|
||||||
|
* Verify you have `with: fetch-depth: 2` in your `actions/checkout@master` 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
|
||||||
|
5. How do I set a upload timeout?
|
||||||
|
* github has a built-in `timeout-minutes` option. Place `timeout-minutes: X` before the `steps:` line. Timeout defaults to 360 minutes.
|
||||||
|
|
||||||
<details id="cant-access-remote-sftp">
|
|
||||||
<summary>Can't access remote 'sftp://', exiting...</summary>
|
|
||||||
|
|
||||||
See ["Failed to upload files"](#failed-to-upload) section above
|
### Debugging locally
|
||||||
</details>
|
##### Instructions for debugging on windows
|
||||||
|
- Install docker for windows
|
||||||
<details id="cant-access-remote-ftps">
|
- Open powershell
|
||||||
<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
|
|
||||||
##### Instructions for debugging Windows
|
|
||||||
- [Install docker](https://docs.docker.com/get-docker/)
|
|
||||||
- Open powershell **as Administrator**
|
|
||||||
- Install [act-cli](https://github.com/nektos/act#installation) by running `choco install act-cli`
|
|
||||||
- Navigate to the repo folder
|
- Navigate to the repo folder
|
||||||
- Run `npm install` - this will install all dependencies to build this project
|
- Run `docker build --tag action .`
|
||||||
- Run `npm build` - this will build the action javascript and watch/rebuild when files change
|
- Run `docker run action`
|
||||||
- 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
|
##### Instructions for debugging on linux
|
||||||
- [Install docker](https://docs.docker.com/get-docker/) on a Debian-based distro you can run `sudo apt install docker docker.io`
|
- Please submit a PR for linux instructions :)
|
||||||
- Open the terminal
|
|
||||||
- Install [act-cli](https://github.com/nektos/act#installation)
|
|
||||||
- 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
|
|
||||||
|
|
||||||
#### Pull Requests Welcome!
|
#### Pull Requests Welcome!
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ inputs:
|
|||||||
required: true
|
required: true
|
||||||
local-dir:
|
local-dir:
|
||||||
description: 'The local folder to copy, defaults to root project folder'
|
description: 'The local folder to copy, defaults to root project folder'
|
||||||
default: ./
|
defaults: ./
|
||||||
required: false
|
required: false
|
||||||
git-ftp-args:
|
git-ftp-args:
|
||||||
description: 'Passes through options into git-ftp'
|
description: 'Passes through options into git-ftp'
|
||||||
default:
|
defaults:
|
||||||
required: false
|
required: false
|
||||||
runs:
|
runs:
|
||||||
using: 'docker'
|
using: 'docker'
|
||||||
|
|||||||
@@ -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
|
|
||||||
717
dist/index.js
vendored
717
dist/index.js
vendored
@@ -43,303 +43,6 @@ module.exports =
|
|||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/******/ ({
|
/******/ ({
|
||||||
|
|
||||||
/***/ 1:
|
|
||||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const childProcess = __webpack_require__(129);
|
|
||||||
const path = __webpack_require__(622);
|
|
||||||
const util_1 = __webpack_require__(669);
|
|
||||||
const ioUtil = __webpack_require__(672);
|
|
||||||
const exec = util_1.promisify(childProcess.exec);
|
|
||||||
/**
|
|
||||||
* Copies a file or folder.
|
|
||||||
* Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
|
|
||||||
*
|
|
||||||
* @param source source path
|
|
||||||
* @param dest destination path
|
|
||||||
* @param options optional. See CopyOptions.
|
|
||||||
*/
|
|
||||||
function cp(source, dest, options = {}) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const { force, recursive } = readCopyOptions(options);
|
|
||||||
const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null;
|
|
||||||
// Dest is an existing file, but not forcing
|
|
||||||
if (destStat && destStat.isFile() && !force) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If dest is an existing directory, should copy inside.
|
|
||||||
const newDest = destStat && destStat.isDirectory()
|
|
||||||
? path.join(dest, path.basename(source))
|
|
||||||
: dest;
|
|
||||||
if (!(yield ioUtil.exists(source))) {
|
|
||||||
throw new Error(`no such file or directory: ${source}`);
|
|
||||||
}
|
|
||||||
const sourceStat = yield ioUtil.stat(source);
|
|
||||||
if (sourceStat.isDirectory()) {
|
|
||||||
if (!recursive) {
|
|
||||||
throw new Error(`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
yield cpDirRecursive(source, newDest, 0, force);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (path.relative(source, newDest) === '') {
|
|
||||||
// a file cannot be copied to itself
|
|
||||||
throw new Error(`'${newDest}' and '${source}' are the same file`);
|
|
||||||
}
|
|
||||||
yield copyFile(source, newDest, force);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.cp = cp;
|
|
||||||
/**
|
|
||||||
* Moves a path.
|
|
||||||
*
|
|
||||||
* @param source source path
|
|
||||||
* @param dest destination path
|
|
||||||
* @param options optional. See MoveOptions.
|
|
||||||
*/
|
|
||||||
function mv(source, dest, options = {}) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (yield ioUtil.exists(dest)) {
|
|
||||||
let destExists = true;
|
|
||||||
if (yield ioUtil.isDirectory(dest)) {
|
|
||||||
// If dest is directory copy src into dest
|
|
||||||
dest = path.join(dest, path.basename(source));
|
|
||||||
destExists = yield ioUtil.exists(dest);
|
|
||||||
}
|
|
||||||
if (destExists) {
|
|
||||||
if (options.force == null || options.force) {
|
|
||||||
yield rmRF(dest);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Destination already exists');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield mkdirP(path.dirname(dest));
|
|
||||||
yield ioUtil.rename(source, dest);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.mv = mv;
|
|
||||||
/**
|
|
||||||
* Remove a path recursively with force
|
|
||||||
*
|
|
||||||
* @param inputPath path to remove
|
|
||||||
*/
|
|
||||||
function rmRF(inputPath) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (ioUtil.IS_WINDOWS) {
|
|
||||||
// Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
|
|
||||||
// program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
|
|
||||||
try {
|
|
||||||
if (yield ioUtil.isDirectory(inputPath, true)) {
|
|
||||||
yield exec(`rd /s /q "${inputPath}"`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
yield exec(`del /f /a "${inputPath}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT')
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
// Shelling out fails to remove a symlink folder with missing source, this unlink catches that
|
|
||||||
try {
|
|
||||||
yield ioUtil.unlink(inputPath);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT')
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let isDir = false;
|
|
||||||
try {
|
|
||||||
isDir = yield ioUtil.isDirectory(inputPath);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT')
|
|
||||||
throw err;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isDir) {
|
|
||||||
yield exec(`rm -rf "${inputPath}"`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
yield ioUtil.unlink(inputPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.rmRF = rmRF;
|
|
||||||
/**
|
|
||||||
* Make a directory. Creates the full path with folders in between
|
|
||||||
* Will throw if it fails
|
|
||||||
*
|
|
||||||
* @param fsPath path to create
|
|
||||||
* @returns Promise<void>
|
|
||||||
*/
|
|
||||||
function mkdirP(fsPath) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
yield ioUtil.mkdirP(fsPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.mkdirP = mkdirP;
|
|
||||||
/**
|
|
||||||
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
|
|
||||||
* If you check and the tool does not exist, it will throw.
|
|
||||||
*
|
|
||||||
* @param tool name of the tool
|
|
||||||
* @param check whether to check if tool exists
|
|
||||||
* @returns Promise<string> path to tool
|
|
||||||
*/
|
|
||||||
function which(tool, check) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (!tool) {
|
|
||||||
throw new Error("parameter 'tool' is required");
|
|
||||||
}
|
|
||||||
// recursive when check=true
|
|
||||||
if (check) {
|
|
||||||
const result = yield which(tool, false);
|
|
||||||
if (!result) {
|
|
||||||
if (ioUtil.IS_WINDOWS) {
|
|
||||||
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// build the list of extensions to try
|
|
||||||
const extensions = [];
|
|
||||||
if (ioUtil.IS_WINDOWS && process.env.PATHEXT) {
|
|
||||||
for (const extension of process.env.PATHEXT.split(path.delimiter)) {
|
|
||||||
if (extension) {
|
|
||||||
extensions.push(extension);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if it's rooted, return it if exists. otherwise return empty.
|
|
||||||
if (ioUtil.isRooted(tool)) {
|
|
||||||
const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions);
|
|
||||||
if (filePath) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// if any path separators, return empty
|
|
||||||
if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// build the list of directories
|
|
||||||
//
|
|
||||||
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
|
|
||||||
// it feels like we should not do this. Checking the current directory seems like more of a use
|
|
||||||
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
|
|
||||||
// across platforms.
|
|
||||||
const directories = [];
|
|
||||||
if (process.env.PATH) {
|
|
||||||
for (const p of process.env.PATH.split(path.delimiter)) {
|
|
||||||
if (p) {
|
|
||||||
directories.push(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return the first match
|
|
||||||
for (const directory of directories) {
|
|
||||||
const filePath = yield ioUtil.tryGetExecutablePath(directory + path.sep + tool, extensions);
|
|
||||||
if (filePath) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
throw new Error(`which failed with message ${err.message}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.which = which;
|
|
||||||
function readCopyOptions(options) {
|
|
||||||
const force = options.force == null ? true : options.force;
|
|
||||||
const recursive = Boolean(options.recursive);
|
|
||||||
return { force, recursive };
|
|
||||||
}
|
|
||||||
function cpDirRecursive(sourceDir, destDir, currentDepth, force) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
// Ensure there is not a run away recursive copy
|
|
||||||
if (currentDepth >= 255)
|
|
||||||
return;
|
|
||||||
currentDepth++;
|
|
||||||
yield mkdirP(destDir);
|
|
||||||
const files = yield ioUtil.readdir(sourceDir);
|
|
||||||
for (const fileName of files) {
|
|
||||||
const srcFile = `${sourceDir}/${fileName}`;
|
|
||||||
const destFile = `${destDir}/${fileName}`;
|
|
||||||
const srcFileStat = yield ioUtil.lstat(srcFile);
|
|
||||||
if (srcFileStat.isDirectory()) {
|
|
||||||
// Recurse
|
|
||||||
yield cpDirRecursive(srcFile, destFile, currentDepth, force);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
yield copyFile(srcFile, destFile, force);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Change the mode for the newly created directory
|
|
||||||
yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Buffered file copy
|
|
||||||
function copyFile(srcFile, destFile, force) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) {
|
|
||||||
// unlink/re-link it
|
|
||||||
try {
|
|
||||||
yield ioUtil.lstat(destFile);
|
|
||||||
yield ioUtil.unlink(destFile);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
// Try to override file permission
|
|
||||||
if (e.code === 'EPERM') {
|
|
||||||
yield ioUtil.chmod(destFile, '0666');
|
|
||||||
yield ioUtil.unlink(destFile);
|
|
||||||
}
|
|
||||||
// other errors = it doesn't exist, no work to do
|
|
||||||
}
|
|
||||||
// Copy over symlink
|
|
||||||
const symlinkFull = yield ioUtil.readlink(srcFile);
|
|
||||||
yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null);
|
|
||||||
}
|
|
||||||
else if (!(yield ioUtil.exists(destFile)) || force) {
|
|
||||||
yield ioUtil.copyFile(srcFile, destFile);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=io.js.map
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 9:
|
/***/ 9:
|
||||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||||
|
|
||||||
@@ -354,20 +57,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
||||||
result["default"] = mod;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const os = __importStar(__webpack_require__(87));
|
const os = __webpack_require__(87);
|
||||||
const events = __importStar(__webpack_require__(614));
|
const events = __webpack_require__(614);
|
||||||
const child = __importStar(__webpack_require__(129));
|
const child = __webpack_require__(129);
|
||||||
const path = __importStar(__webpack_require__(622));
|
|
||||||
const io = __importStar(__webpack_require__(1));
|
|
||||||
const ioUtil = __importStar(__webpack_require__(672));
|
|
||||||
/* eslint-disable @typescript-eslint/unbound-method */
|
/* eslint-disable @typescript-eslint/unbound-method */
|
||||||
const IS_WINDOWS = process.platform === 'win32';
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
/*
|
/*
|
||||||
@@ -713,16 +406,6 @@ class ToolRunner extends events.EventEmitter {
|
|||||||
*/
|
*/
|
||||||
exec() {
|
exec() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// root the tool path if it is unrooted and contains relative pathing
|
|
||||||
if (!ioUtil.isRooted(this.toolPath) &&
|
|
||||||
(this.toolPath.includes('/') ||
|
|
||||||
(IS_WINDOWS && this.toolPath.includes('\\')))) {
|
|
||||||
// prefer options.cwd if it is specified, however options.cwd may also need to be rooted
|
|
||||||
this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
|
|
||||||
}
|
|
||||||
// if the tool is only a file name, then resolve it from the PATH
|
|
||||||
// otherwise verify it exists (add extension on Windows if necessary)
|
|
||||||
this.toolPath = yield io.which(this.toolPath, true);
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._debug(`exec tool: ${this.toolPath}`);
|
this._debug(`exec tool: ${this.toolPath}`);
|
||||||
this._debug('arguments:');
|
this._debug('arguments:');
|
||||||
@@ -811,12 +494,6 @@ class ToolRunner extends events.EventEmitter {
|
|||||||
resolve(exitCode);
|
resolve(exitCode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (this.options.input) {
|
|
||||||
if (!cp.stdin) {
|
|
||||||
throw new Error('child process missing stdin');
|
|
||||||
}
|
|
||||||
cp.stdin.end(this.options.input);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -982,58 +659,30 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||||||
result["default"] = mod;
|
result["default"] = mod;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const core = __importStar(__webpack_require__(470));
|
const core = __importStar(__webpack_require__(470));
|
||||||
const exec = __importStar(__webpack_require__(986));
|
const exec = __importStar(__webpack_require__(986));
|
||||||
const fs_1 = __importDefault(__webpack_require__(747));
|
|
||||||
const util_1 = __webpack_require__(669);
|
|
||||||
const writeFileAsync = util_1.promisify(fs_1.default.writeFile);
|
|
||||||
const errorDeploying = "⚠️ Error deploying";
|
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const userArguments = getUserArguments();
|
||||||
try {
|
try {
|
||||||
const userArguments = getUserArguments();
|
|
||||||
yield configureHost(userArguments);
|
|
||||||
yield syncFiles(userArguments);
|
yield syncFiles(userArguments);
|
||||||
console.log("✅ Deploy Complete");
|
console.log("✅ Deploy Complete");
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(errorDeploying);
|
console.error("⚠️ Error deploying");
|
||||||
core.setFailed(error.message);
|
core.setFailed(error.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
run();
|
run();
|
||||||
function configureHost(args) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (args.knownHosts === "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const sshFolder = `${process.env['HOME']}/.ssh`;
|
|
||||||
yield exec.exec(`mkdir -v -p ${sshFolder}`);
|
|
||||||
yield exec.exec(`chmod 700 ${sshFolder}`);
|
|
||||||
writeFileAsync(`${sshFolder}/known_hosts`, args.knownHosts);
|
|
||||||
yield 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() {
|
function getUserArguments() {
|
||||||
return {
|
return {
|
||||||
ftp_server: core.getInput("ftp-server", { required: true }),
|
ftp_server: core.getInput("ftp-server", { required: true }),
|
||||||
ftp_username: core.getInput("ftp-username", { required: true }),
|
ftp_username: core.getInput("ftp-username", { required: true }),
|
||||||
ftp_password: core.getInput("ftp-password", { required: true }),
|
ftp_password: core.getInput("ftp-password", { required: true }),
|
||||||
local_dir: withDefault(core.getInput("local-dir"), "./"),
|
local_dir: withDefault(core.getInput("local-dir"), "./"),
|
||||||
gitFtpArgs: withDefault(core.getInput("git-ftp-args"), ""),
|
gitFtpArgs: withDefault(core.getInput("git-ftp-args"), "")
|
||||||
knownHosts: withDefault(core.getInput("known-hosts"), "")
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function withDefault(value, defaultValue) {
|
function withDefault(value, defaultValue) {
|
||||||
@@ -1049,32 +698,18 @@ function syncFiles(args) {
|
|||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
yield core.group("Uploading files", () => __awaiter(this, void 0, void 0, function* () {
|
yield core.group("Uploading files", () => __awaiter(this, void 0, void 0, function* () {
|
||||||
return yield exec.exec("git ftp push", [
|
return yield 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}`);
|
||||||
"--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) {
|
||||||
|
console.error("⚠️ Failed to upload files");
|
||||||
core.setFailed(error.message);
|
core.setFailed(error.message);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 357:
|
|
||||||
/***/ (function(module) {
|
|
||||||
|
|
||||||
module.exports = require("assert");
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 431:
|
/***/ 431:
|
||||||
@@ -1082,24 +717,17 @@ module.exports = require("assert");
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
||||||
result["default"] = mod;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const os = __importStar(__webpack_require__(87));
|
const os = __webpack_require__(87);
|
||||||
/**
|
/**
|
||||||
* Commands
|
* Commands
|
||||||
*
|
*
|
||||||
* Command Format:
|
* Command Format:
|
||||||
* ::name key=value,key=value::message
|
* ##[name key=value;key=value]message
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* ::warning::This is the message
|
* ##[warning]This is the user warning message
|
||||||
* ::set-env name=MY_VAR::some value
|
* ##[set-secret name=mypassword]definitelyNotAPassword!
|
||||||
*/
|
*/
|
||||||
function issueCommand(command, properties, message) {
|
function issueCommand(command, properties, message) {
|
||||||
const cmd = new Command(command, properties, message);
|
const cmd = new Command(command, properties, message);
|
||||||
@@ -1124,53 +752,34 @@ class Command {
|
|||||||
let cmdStr = CMD_STRING + this.command;
|
let cmdStr = CMD_STRING + this.command;
|
||||||
if (this.properties && Object.keys(this.properties).length > 0) {
|
if (this.properties && Object.keys(this.properties).length > 0) {
|
||||||
cmdStr += ' ';
|
cmdStr += ' ';
|
||||||
let first = true;
|
|
||||||
for (const key in this.properties) {
|
for (const key in this.properties) {
|
||||||
if (this.properties.hasOwnProperty(key)) {
|
if (this.properties.hasOwnProperty(key)) {
|
||||||
const val = this.properties[key];
|
const val = this.properties[key];
|
||||||
if (val) {
|
if (val) {
|
||||||
if (first) {
|
// safely append the val - avoid blowing up when attempting to
|
||||||
first = false;
|
// call .replace() if message is not a string for some reason
|
||||||
}
|
cmdStr += `${key}=${escape(`${val || ''}`)},`;
|
||||||
else {
|
|
||||||
cmdStr += ',';
|
|
||||||
}
|
|
||||||
cmdStr += `${key}=${escapeProperty(val)}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
|
cmdStr += CMD_STRING;
|
||||||
|
// safely append the message - avoid blowing up when attempting to
|
||||||
|
// call .replace() if message is not a string for some reason
|
||||||
|
const message = `${this.message || ''}`;
|
||||||
|
cmdStr += escapeData(message);
|
||||||
return cmdStr;
|
return cmdStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Sanitizes an input into a string so it can be passed into issueCommand safely
|
|
||||||
* @param input input to sanitize into a string
|
|
||||||
*/
|
|
||||||
function toCommandValue(input) {
|
|
||||||
if (input === null || input === undefined) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
else if (typeof input === 'string' || input instanceof String) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
return JSON.stringify(input);
|
|
||||||
}
|
|
||||||
exports.toCommandValue = toCommandValue;
|
|
||||||
function escapeData(s) {
|
function escapeData(s) {
|
||||||
return toCommandValue(s)
|
return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
||||||
.replace(/%/g, '%25')
|
|
||||||
.replace(/\r/g, '%0D')
|
|
||||||
.replace(/\n/g, '%0A');
|
|
||||||
}
|
}
|
||||||
function escapeProperty(s) {
|
function escape(s) {
|
||||||
return toCommandValue(s)
|
return s
|
||||||
.replace(/%/g, '%25')
|
|
||||||
.replace(/\r/g, '%0D')
|
.replace(/\r/g, '%0D')
|
||||||
.replace(/\n/g, '%0A')
|
.replace(/\n/g, '%0A')
|
||||||
.replace(/:/g, '%3A')
|
.replace(/]/g, '%5D')
|
||||||
.replace(/,/g, '%2C');
|
.replace(/;/g, '%3B');
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=command.js.map
|
//# sourceMappingURL=command.js.map
|
||||||
|
|
||||||
@@ -1190,17 +799,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
||||||
result["default"] = mod;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const command_1 = __webpack_require__(431);
|
const command_1 = __webpack_require__(431);
|
||||||
const os = __importStar(__webpack_require__(87));
|
const os = __webpack_require__(87);
|
||||||
const path = __importStar(__webpack_require__(622));
|
const path = __webpack_require__(622);
|
||||||
/**
|
/**
|
||||||
* The code to exit an action
|
* The code to exit an action
|
||||||
*/
|
*/
|
||||||
@@ -1221,13 +823,11 @@ var ExitCode;
|
|||||||
/**
|
/**
|
||||||
* Sets env variable for this action and future actions in the job
|
* Sets env variable for this action and future actions in the job
|
||||||
* @param name the name of the variable to set
|
* @param name the name of the variable to set
|
||||||
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
|
* @param val the value of the variable
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function exportVariable(name, val) {
|
function exportVariable(name, val) {
|
||||||
const convertedVal = command_1.toCommandValue(val);
|
process.env[name] = val;
|
||||||
process.env[name] = convertedVal;
|
command_1.issueCommand('set-env', { name }, val);
|
||||||
command_1.issueCommand('set-env', { name }, convertedVal);
|
|
||||||
}
|
}
|
||||||
exports.exportVariable = exportVariable;
|
exports.exportVariable = exportVariable;
|
||||||
/**
|
/**
|
||||||
@@ -1266,22 +866,12 @@ exports.getInput = getInput;
|
|||||||
* Sets the value of an output.
|
* Sets the value of an output.
|
||||||
*
|
*
|
||||||
* @param name name of the output to set
|
* @param name name of the output to set
|
||||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
* @param value value to store
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function setOutput(name, value) {
|
function setOutput(name, value) {
|
||||||
command_1.issueCommand('set-output', { name }, value);
|
command_1.issueCommand('set-output', { name }, value);
|
||||||
}
|
}
|
||||||
exports.setOutput = setOutput;
|
exports.setOutput = setOutput;
|
||||||
/**
|
|
||||||
* Enables or disables the echoing of commands into stdout for the rest of the step.
|
|
||||||
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function setCommandEcho(enabled) {
|
|
||||||
command_1.issue('echo', enabled ? 'on' : 'off');
|
|
||||||
}
|
|
||||||
exports.setCommandEcho = setCommandEcho;
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
// Results
|
// Results
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@@ -1298,13 +888,6 @@ exports.setFailed = setFailed;
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
// Logging Commands
|
// Logging Commands
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
/**
|
|
||||||
* Gets whether Actions Step Debug is on or not
|
|
||||||
*/
|
|
||||||
function isDebug() {
|
|
||||||
return process.env['RUNNER_DEBUG'] === '1';
|
|
||||||
}
|
|
||||||
exports.isDebug = isDebug;
|
|
||||||
/**
|
/**
|
||||||
* Writes debug message to user log
|
* Writes debug message to user log
|
||||||
* @param message debug message
|
* @param message debug message
|
||||||
@@ -1315,18 +898,18 @@ function debug(message) {
|
|||||||
exports.debug = debug;
|
exports.debug = debug;
|
||||||
/**
|
/**
|
||||||
* Adds an error issue
|
* Adds an error issue
|
||||||
* @param message error issue message. Errors will be converted to string via toString()
|
* @param message error issue message
|
||||||
*/
|
*/
|
||||||
function error(message) {
|
function error(message) {
|
||||||
command_1.issue('error', message instanceof Error ? message.toString() : message);
|
command_1.issue('error', message);
|
||||||
}
|
}
|
||||||
exports.error = error;
|
exports.error = error;
|
||||||
/**
|
/**
|
||||||
* Adds an warning issue
|
* Adds an warning issue
|
||||||
* @param message warning issue message. Errors will be converted to string via toString()
|
* @param message warning issue message
|
||||||
*/
|
*/
|
||||||
function warning(message) {
|
function warning(message) {
|
||||||
command_1.issue('warning', message instanceof Error ? message.toString() : message);
|
command_1.issue('warning', message);
|
||||||
}
|
}
|
||||||
exports.warning = warning;
|
exports.warning = warning;
|
||||||
/**
|
/**
|
||||||
@@ -1384,9 +967,8 @@ exports.group = group;
|
|||||||
* Saves state for current action, the state can only be retrieved by this action's post job execution.
|
* Saves state for current action, the state can only be retrieved by this action's post job execution.
|
||||||
*
|
*
|
||||||
* @param name name of the state to store
|
* @param name name of the state to store
|
||||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
* @param value value to store
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function saveState(name, value) {
|
function saveState(name, value) {
|
||||||
command_1.issueCommand('save-state', { name }, value);
|
command_1.issueCommand('save-state', { name }, value);
|
||||||
}
|
}
|
||||||
@@ -1419,222 +1001,6 @@ module.exports = require("path");
|
|||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 669:
|
|
||||||
/***/ (function(module) {
|
|
||||||
|
|
||||||
module.exports = require("util");
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 672:
|
|
||||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var _a;
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const assert_1 = __webpack_require__(357);
|
|
||||||
const fs = __webpack_require__(747);
|
|
||||||
const path = __webpack_require__(622);
|
|
||||||
_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink;
|
|
||||||
exports.IS_WINDOWS = process.platform === 'win32';
|
|
||||||
function exists(fsPath) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
try {
|
|
||||||
yield exports.stat(fsPath);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.exists = exists;
|
|
||||||
function isDirectory(fsPath, useStat = false) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const stats = useStat ? yield exports.stat(fsPath) : yield exports.lstat(fsPath);
|
|
||||||
return stats.isDirectory();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.isDirectory = isDirectory;
|
|
||||||
/**
|
|
||||||
* On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
|
|
||||||
* \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
|
|
||||||
*/
|
|
||||||
function isRooted(p) {
|
|
||||||
p = normalizeSeparators(p);
|
|
||||||
if (!p) {
|
|
||||||
throw new Error('isRooted() parameter "p" cannot be empty');
|
|
||||||
}
|
|
||||||
if (exports.IS_WINDOWS) {
|
|
||||||
return (p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello
|
|
||||||
); // e.g. C: or C:\hello
|
|
||||||
}
|
|
||||||
return p.startsWith('/');
|
|
||||||
}
|
|
||||||
exports.isRooted = isRooted;
|
|
||||||
/**
|
|
||||||
* Recursively create a directory at `fsPath`.
|
|
||||||
*
|
|
||||||
* This implementation is optimistic, meaning it attempts to create the full
|
|
||||||
* path first, and backs up the path stack from there.
|
|
||||||
*
|
|
||||||
* @param fsPath The path to create
|
|
||||||
* @param maxDepth The maximum recursion depth
|
|
||||||
* @param depth The current recursion depth
|
|
||||||
*/
|
|
||||||
function mkdirP(fsPath, maxDepth = 1000, depth = 1) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
assert_1.ok(fsPath, 'a path argument must be provided');
|
|
||||||
fsPath = path.resolve(fsPath);
|
|
||||||
if (depth >= maxDepth)
|
|
||||||
return exports.mkdir(fsPath);
|
|
||||||
try {
|
|
||||||
yield exports.mkdir(fsPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
switch (err.code) {
|
|
||||||
case 'ENOENT': {
|
|
||||||
yield mkdirP(path.dirname(fsPath), maxDepth, depth + 1);
|
|
||||||
yield exports.mkdir(fsPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
let stats;
|
|
||||||
try {
|
|
||||||
stats = yield exports.stat(fsPath);
|
|
||||||
}
|
|
||||||
catch (err2) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
if (!stats.isDirectory())
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.mkdirP = mkdirP;
|
|
||||||
/**
|
|
||||||
* Best effort attempt to determine whether a file exists and is executable.
|
|
||||||
* @param filePath file path to check
|
|
||||||
* @param extensions additional file extensions to try
|
|
||||||
* @return if file exists and is executable, returns the file path. otherwise empty string.
|
|
||||||
*/
|
|
||||||
function tryGetExecutablePath(filePath, extensions) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let stats = undefined;
|
|
||||||
try {
|
|
||||||
// test file exists
|
|
||||||
stats = yield exports.stat(filePath);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err.code !== 'ENOENT') {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stats && stats.isFile()) {
|
|
||||||
if (exports.IS_WINDOWS) {
|
|
||||||
// on Windows, test for valid extension
|
|
||||||
const upperExt = path.extname(filePath).toUpperCase();
|
|
||||||
if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (isUnixExecutable(stats)) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// try each extension
|
|
||||||
const originalFilePath = filePath;
|
|
||||||
for (const extension of extensions) {
|
|
||||||
filePath = originalFilePath + extension;
|
|
||||||
stats = undefined;
|
|
||||||
try {
|
|
||||||
stats = yield exports.stat(filePath);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err.code !== 'ENOENT') {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Unexpected error attempting to determine if executable file exists '${filePath}': ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stats && stats.isFile()) {
|
|
||||||
if (exports.IS_WINDOWS) {
|
|
||||||
// preserve the case of the actual file (since an extension was appended)
|
|
||||||
try {
|
|
||||||
const directory = path.dirname(filePath);
|
|
||||||
const upperName = path.basename(filePath).toUpperCase();
|
|
||||||
for (const actualName of yield exports.readdir(directory)) {
|
|
||||||
if (upperName === actualName.toUpperCase()) {
|
|
||||||
filePath = path.join(directory, actualName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(`Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}`);
|
|
||||||
}
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (isUnixExecutable(stats)) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.tryGetExecutablePath = tryGetExecutablePath;
|
|
||||||
function normalizeSeparators(p) {
|
|
||||||
p = p || '';
|
|
||||||
if (exports.IS_WINDOWS) {
|
|
||||||
// convert slashes on Windows
|
|
||||||
p = p.replace(/\//g, '\\');
|
|
||||||
// remove redundant slashes
|
|
||||||
return p.replace(/\\\\+/g, '\\');
|
|
||||||
}
|
|
||||||
// remove redundant slashes
|
|
||||||
return p.replace(/\/\/+/g, '/');
|
|
||||||
}
|
|
||||||
// on Mac/Linux, test the execute bit
|
|
||||||
// R W X R W X R W X
|
|
||||||
// 256 128 64 32 16 8 4 2 1
|
|
||||||
function isUnixExecutable(stats) {
|
|
||||||
return ((stats.mode & 1) > 0 ||
|
|
||||||
((stats.mode & 8) > 0 && stats.gid === process.getgid()) ||
|
|
||||||
((stats.mode & 64) > 0 && stats.uid === process.getuid()));
|
|
||||||
}
|
|
||||||
//# sourceMappingURL=io-util.js.map
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 747:
|
|
||||||
/***/ (function(module) {
|
|
||||||
|
|
||||||
module.exports = require("fs");
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 986:
|
/***/ 986:
|
||||||
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||||
|
|
||||||
@@ -1649,15 +1015,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
||||||
result["default"] = mod;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const tr = __importStar(__webpack_require__(9));
|
const tr = __webpack_require__(9);
|
||||||
/**
|
/**
|
||||||
* Exec a command.
|
* Exec a command.
|
||||||
* Output will be streamed to the live console.
|
* Output will be streamed to the live console.
|
||||||
|
|||||||
BIN
images/action-preview.gif
Normal file
BIN
images/action-preview.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
3723
package-lock.json
generated
3723
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "typescript-action",
|
"name": "typescript-action",
|
||||||
"version": "3.1.2",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "TypeScript template action",
|
"description": "TypeScript template action",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ncc build src/main.ts -o dist --watch",
|
"build:dev": "tsc",
|
||||||
"build-docker": "docker build --tag action .",
|
"build:production": "ncc build src/main.ts -o dist",
|
||||||
"run-docker": "act --workflows ./debug/ --secret username=UserNameHere --secret password=PasswordHere"
|
"build:docker": "docker build --tag action .",
|
||||||
|
"run:docker": "docker run action --build-arg FTP_SERVER=example.com FTP_USERNAME=test@example.com FTP_PASSWORD=passwordExample"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -24,16 +25,16 @@
|
|||||||
"author": "SamKirkland",
|
"author": "SamKirkland",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "1.2.4",
|
"@actions/core": "1.2.0",
|
||||||
"@actions/exec": "1.0.4"
|
"@actions/exec": "1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "25.2.1",
|
"@types/jest": "24.0.23",
|
||||||
"@types/node": "12.12.8",
|
"@types/node": "12.12.8",
|
||||||
"@zeit/ncc": "0.22.1",
|
"@zeit/ncc": "0.20.5",
|
||||||
"jest": "25.5.3",
|
"jest": "24.9.0",
|
||||||
"jest-circus": "25.5.3",
|
"jest-circus": "24.9.0",
|
||||||
"ts-jest": "25.4.0",
|
"ts-jest": "24.1.0",
|
||||||
"typescript": "3.8.3"
|
"typescript": "3.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/main.ts
51
src/main.ts
@@ -1,49 +1,23 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import fs from 'fs';
|
|
||||||
import { promisify } from 'util';
|
|
||||||
import { IActionArguments } from './types';
|
import { IActionArguments } from './types';
|
||||||
|
|
||||||
const writeFileAsync = promisify(fs.writeFile);
|
|
||||||
const errorDeploying = "⚠️ Error deploying";
|
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
const userArguments = getUserArguments();
|
||||||
const userArguments = getUserArguments();
|
|
||||||
|
|
||||||
await configureHost(userArguments);
|
try {
|
||||||
await syncFiles(userArguments);
|
await syncFiles(userArguments);
|
||||||
|
|
||||||
console.log("✅ Deploy Complete");
|
console.log("✅ Deploy Complete");
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error(errorDeploying);
|
console.error("⚠️ Error deploying");
|
||||||
core.setFailed(error.message);
|
core.setFailed(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
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 {
|
function getUserArguments(): IActionArguments {
|
||||||
return {
|
return {
|
||||||
@@ -51,8 +25,7 @@ function getUserArguments(): IActionArguments {
|
|||||||
ftp_username: core.getInput("ftp-username", { required: true }),
|
ftp_username: core.getInput("ftp-username", { required: true }),
|
||||||
ftp_password: core.getInput("ftp-password", { required: true }),
|
ftp_password: core.getInput("ftp-password", { required: true }),
|
||||||
local_dir: withDefault(core.getInput("local-dir"), "./"),
|
local_dir: withDefault(core.getInput("local-dir"), "./"),
|
||||||
gitFtpArgs: withDefault(core.getInput("git-ftp-args"), ""),
|
gitFtpArgs: withDefault(core.getInput("git-ftp-args"), "")
|
||||||
knownHosts: withDefault(core.getInput("known-hosts"), "")
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,22 +43,12 @@ function withDefault(value: string, defaultValue: string) {
|
|||||||
async function syncFiles(args: IActionArguments) {
|
async function syncFiles(args: IActionArguments) {
|
||||||
try {
|
try {
|
||||||
await core.group("Uploading files", async () => {
|
await core.group("Uploading files", async () => {
|
||||||
return await exec.exec(
|
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}`);
|
||||||
"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) {
|
||||||
|
console.error("⚠️ Failed to upload files");
|
||||||
core.setFailed(error.message);
|
core.setFailed(error.message);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,6 @@ export interface IActionArguments {
|
|||||||
|
|
||||||
/** @default "" */
|
/** @default "" */
|
||||||
gitFtpArgs: string | undefined;
|
gitFtpArgs: string | undefined;
|
||||||
|
|
||||||
/** @default "" */
|
|
||||||
knownHosts: string | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +22,7 @@ export enum gitFTPExitCode {
|
|||||||
ErrorWhileDownloading = 5,
|
ErrorWhileDownloading = 5,
|
||||||
UnknownProtocol = 6,
|
UnknownProtocol = 6,
|
||||||
RemoteLocked = 7,
|
RemoteLocked = 7,
|
||||||
GitRelatedError = 8,
|
NotAGitProject = 8,
|
||||||
PreFTPPushHookFailed = 9,
|
PreFTPPushHookFailed = 9,
|
||||||
LocalFileOperationFailed = 10
|
LocalFileOperationFailed = 10
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Migrating from v2 to v3
|
# Migrating from v2 to v3
|
||||||
|
|
||||||
`uses: actions/checkout@v2.1.0` must now have the option `fetch-depth: 2`
|
`uses: actions/checkout@master` must now have the option `fetch-depth: 2`
|
||||||
|
|
||||||
Without the `fetch-depth` option diffs cannot be calculated and all files will be uploaded.
|
Without the `fetch-depth` option diffs cannot be calculated and all files will be uploaded.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user