deployphp / action

GitHub Action for Deployer
MIT License
220 stars 44 forks source link

Error: Command failed with exit code 1: ssh-agent #22

Open mikebrandl opened 2 years ago

mikebrandl commented 2 years ago

Hi

I have a standard laravel deployment working locally. Because the server I want to deploy to is behind a strict firewall I want to run a self hosted github runner to deploy to it.

However when I attempt to deploy I get the following:

Error: Command failed with exit code 1: ssh-agent -a /tmp/ssh-auth.sock unix_listener: cannot bind to path /tmp/ssh-auth.sock: Address already in use

I understand what the errors means, but is there something I can change as a workaround?

Thanks

Mike

Upvote & Fund

Fund with Polar

ngrie commented 2 years ago

We fixed this in our pipeline by adding another step to the workflow job:

- name: Clean-up
  if: always()
  run: killall ssh-agent

While this works, I think the proper solution would be to add a similar clean-up step to the deployer action itself.

antonmedv commented 2 years ago

I didn’t get it. Action works for me. Do you have some special steps?

ngrie commented 2 years ago

@antonmedv The important difference is:

I want to run a self hosted github runner to deploy to it

GitHub's hosted runners are VMs that are destroyed after every run. For self-hosted runners this is not the case, so that processes and files remain from previous runs. That is the reason why it is best practice for actions to do proper clean up.

Especially for this action it might be a common need to run it self-hosted to deploy to environments with IP based restrictions.

antonmedv commented 2 years ago

Make sense. Is there a way to automate this?

Sn0wCrack commented 1 year ago

@antonmedv

Since you're using execa to run ssh-agent, it looks like you should be able to pass a AbortController as a parameter when executing a new process.

const abortController = new AbortController();
const subprocess = execa('node', [], {signal: abortController.signal});

setTimeout(() => {
    abortController.abort();
}, 1000);

try {
    await subprocess;
} catch (error) {
    console.log(subprocess.killed); // true
    console.log(error.isCanceled); // true
}

This should allow you to trigger the abort after running the deploy action, thus killing the SSH agent.

It looks like as well you can simply send a SIGTERM signal to the process:

const subprocess = execa('node');

setTimeout(() => {
    subprocess.kill('SIGTERM', {
        forceKillAfterTimeout: 2000
    });
}, 1000);

One other recommendation I can give is maybe ensuring you're not modifying anything related to the home directory of a user. All self-hosted runners run under a single user that is re-used for all actions on that runner.

So, while GitHub Actions in the cloud are run on uniquely spawned instances, self-hosted runners are constant, so actions should aim to be as idempotent as possible to ensure compatibility with self-hosted runners where possible.

So while running ssh-agent is probably fine, maybe randomising the temporary socket name, as well as avoiding modifying the default known_hosts should also be done as a way to improve compatibility with self-hosted runners.

EDIT:

Another issue I found is running multiple times causes the ~/.ssh/config file to become invalid, as the StrictHostKeyChecking setting gets added on the same line, which ends up making it look like this:

StrictHostKeyChecking onStrictHostKeyChecking on

My current workaround is also removing the ~/.ssh/config file post-deployment as well since I don't rely on it at all.

asychev commented 1 year ago

Faced the same issue trying to handle GHA workflow cancellations: you can't run two Deployer steps, second one will fail with the same issue. Would be great to have this part more customizable.

gutschik commented 1 year ago

I was able to fix the problem by setting skip-ssh-setup: true

      - name: deployphp/action
        uses: deployphp/action@v1
        with:
          dep: deploy
          # Private key for connecting to remote hosts. To generate private key:
          # `ssh-keygen -o -t rsa -C 'action@deployer.org'`.
          # Optional
          private-key: ${{ secrets.PRIVATE_KEY }}
          ssh-config: |
            Host *
              StrictHostKeyChecking no
          # Option to skip over the SSH setup/configuration.
          # Self-hosted runners don't need the SSH configuration or the SSH agent
          # to be started.
          # Optional.
          skip-ssh-setup: true
          # You can specify the output verbosity level.
          # Optional. Defaults to -v.
          verbosity: -vvv