How do I delete a file via FTP after deployment?

Trying to build and deploy an ASp.NET Blazor web site.

I have most of the build and deploy action working, but am stuck with the last step. I am using 3rd party hosting, where the only access I have to the server is either a control panel or FTP. Simply FTPing the files to the server doesn’t work, as IIS locks some of them, so it seems I need to upload a file named app_offline.htm first, then upload the site files, then delete app_offline.htm. It’s that last step that’s causing me problems.

The relevant parts of my current .yml file are shown below (you can see the full file here if it helps).

First question is, is this the best way to do it at all? It seems very convoluted. I couldn’t find any way of FTPing a single file, only a folder, so had to create a folder, then create an app_offline.htm file in it, then upload the folder. Is there a simpler way to do this?

The bigger problem is that the last step fails with the following message…

Run StephanThierry/ftp-delete-action@releases/v1
  with:
    host: ***
    user: ***
    password: ***
    remoteFiles: TmpFolder/app_offline.htm
    ignoreSSL: 1
    workingDir: /
  env:
    DOTNET_ROOT: C:\Users\runneradmin\AppData\Local\Microsoft\dotnet
Error: Container action is only supported on Linux

I am running the build and deploy under Windows, and the server is running Windows. I’m not sure how to get around this.

Thanks for any guidance. I’m very new to GitHub actions, so please feel free to suggest any improvements I could make.

Here are the relevant parts of the .yml file…

    - name: Create TmpFolder
      run: mkdir TmpFolder

    - name: Create app_offline.htm
      run: echo "Updating, please wait" > TmpFolder/app_offline.htm

    - name: Upload app_offline.htm
      uses: SamKirkland/FTP-Deploy-Action@4.0.0
      with:
        server: ${{ secrets.PIXATATESTFTPSERVER }}
        username: ${{ secrets.PIXATATESTFTPUSER }}
        password: ${{ secrets.PIXATATESTFTPPASSWORD }}
        local-dir: ./TmpFolder/

    - name: Publish Pixata.Blazor.Sample
      run: dotnet publish Pixata.Blazor.Sample\Pixata.Blazor.Sample.csproj --framework net5.0 --output Pixata.Blazor.Sample.Publish

    - name: Deploy Pixata.Blazor.Sample
      uses: SamKirkland/FTP-Deploy-Action@4.0.0
      with:
        server: ${{ secrets.PIXATATESTFTPSERVER }}
        username: ${{ secrets.PIXATATESTFTPUSER }}
        password: ${{ secrets.PIXATATESTFTPPASSWORD }}
        local-dir: ./Pixata.Blazor.Sample.Publish/

    - name: Delete TmpFolder
      uses: StephanThierry/ftp-delete-action@releases/v1
      with:
        host: ${{ secrets.PIXATATESTFTPSERVER }}
        user: ${{ secrets.PIXATATESTFTPUSER }}
        password: ${{ secrets.PIXATATESTFTPPASSWORD }}
        remoteFiles: "TmpFolder/app_offline.htm"
        #workingDir: "/public_html"
        ignoreSSL: "1"

It looks like the action StephanThierry/ftp-delete-action you’re using is a Docker container action. Docker container actions are currently only supported when your workflow is run on a Linux runner:

You’ll either need to replace any steps in your workflow that run Docker actions or you’ll need to run your workflow on a Linux runner instead.

Thanks for the reply.

Unfortunately, I can’t build and deploy on a Linux runner, as they don’t seem to handle Blazor very well (I’m not the first to spot this).

Do you know of any way I can get around this? This is such a common issue with IIS, I can’t believe I’m the first person to want to solve it, but I’ve searched for hours, and just can’t find anyone who shows how to do it.

Although my current code seems clunky, if I could find a way to delete or rename the file via FTP, it would work. Surely there must be some way of doing this on a Windows machine?

Thanks again.

My first thought is that Python is available on the Windows runners, so you could use the ftplib module.

@airtower-luna Thanks for the suggestion. As I’m completely new at actions, please could you give me some guidance as to how I would use that module in my yml file?

Specifically, would I need to do anything to get Python running, and how do I use the module in the yml?

Thanks again.

Two options:

  1. Write a Python script file that does what you need, commit that to your repository, and call it from a run step.
  2. You can specify a custom shell for a run step, and you can use python for that. In that case you can put the Python code directly into your YAML file:
steps:
  - name: clean up via FTP
    shell: python
    run: |
      import ftplib
      with FTP('ftp.example.com') as ftp:
          ftp.login()
          ftp.delete('somefile.txt')

I’m not sure how the Windows runners are set up, but according to the environment documentation Python is already installed, so you shouldn’t need to do anything. If you need a specific version you might want to run actions/setup-python first (but I don’t see why you would need to).

1 Like

Thanks for the reply. I tried this, but couldn’t get it to work. Here is the test .yml file I have…

name: Test FTP rename

on:  
  push:  
    branches: [ master ]
    
jobs:
  Rename_File: 
    runs-on: windows-latest
    steps:
    - name: rename file
      shell: python
      run: |
        import ftplib
        with FTP(${{ secrets.PIXATATESTFTPSERVER }}) as ftp:
            ftp.login(${{ secrets.PIXATATESTFTPUSER }}, ${{ secrets.PIXATATESTFTPPASSWORD }})
            ftp.delete('jim.txt')

When I run it, it fails with the following error…

Run import ftplib
File “D:\a_temp\f82d80e5-b14a-433e-919d-2c7684cbf258.py”, line 2
with FTP(***) as ftp:
^
SyntaxError: invalid syntax
Error: Process completed with exit code 1.

The “^” was underneath the “ftp:” at the end of the “with” line.

If I add single quotes around the secrets, then the error changes to…

Run import ftplib
import ftplib
with FTP(‘’) as ftp:
ftp.login('
’, ‘’)
ftp.delete(‘jim.txt’)
shell: C:\hostedtoolcache\windows\Python\3.7.9\x64\python.EXE {0}
Traceback (most recent call last):
File “D:\a_temp\0d49b741-1d41-4988-b300-48887b8c449f.py”, line 2, in
with FTP('
’) as ftp:
NameError: name ‘FTP’ is not defined
Error: Process completed with exit code 1.

Any ideas? Thanks again.

OK, ignore the previous. Seems the syntax was wrong. I changed the code to…

    steps:
    - name: rename file
      shell: python
      run: |
        import ftplib
        ftp = ftplib.FTP('${{ secrets.PIXATATESTFTPSERVER }}')
        ftp.login('${{ secrets.PIXATATESTFTPUSER }}', '${{ secrets.PIXATATESTFTPPASSWORD }}')
        ftp.delete('jim.txt')
        ftp.quit()

…and it worked fine.

Thanks again

1 Like

I’m glad it works, I’d recommend using with though to make sure the connection is terminated properly even on errors. :wink:

1 Like

OK, thanks for the tip. Thanks again for all the help.

1 Like