getmeli / meli

Platform for deploying static sites and frontend applications easily. Automatic SSL, deploy previews, reverse proxy, and more.
Other
2.4k stars 97 forks source link

Add "delete" command to the CLI #167

Open queicherius opened 3 years ago

queicherius commented 3 years ago

I would like to see a "delete" command added to the Meli CLI that deletes the deploy of a specific branch. In my use-case, which is deploying branches via CI as previews, each PR generates a deploy, and I would like to be able to automatically clean it up once the PR gets closed. Right now I have to head over into the UI to click the delete button.

gempain commented 3 years ago

Aww, yup, that's a good one too. Just quickly brainstorming before going deeper, how would we detect that the PR has been closed ? When the PR is merged, a CI pipeline runs for the base branch, and in there I don't think we can tell which PR has been merged, do we ? GH Actions sets GITHUB_HEAD_REF and GITHUB_BASE_REF as an environment variable, but I don't think their available once running on the base branch.

We could add a --delete-branch CLI option which would let you specify the branches to delete, but it feels bit unsafe, no ? I mean, you still have to get access to the token, which indicates you have access to the site, but that leak could come from other places.

The cleanest approach I can think of is to setup webhooks on your Git server to let Meli know when a PR is closed, but I don't want to create this dependency at the moment, it's too early and there's a bunch of test coverage to increase before adding extra dependencies.

Do you have an idea of how we would implement this ?

queicherius commented 3 years ago

I was thinking of having a separate workflow, similar to this (untested, pseudo-code)

name: Continuous Integration

on:
  pull_request:
    types: [closed]

jobs:
  cleanup:
    name: 'Cleanup'
    runs-on: ubuntu-latest

    steps:
      - name: 'Cleanup deploy (documentation)'
        run: |
          npx @getmeli/cli@next delete \
            --url "https://meli.company.io" \
            --site "$MELI_SITE" \
            --token "$MELI_TOKEN" \
            --branch "$GITHUB_BRANCH"
        env:
          MELI_SITE: '<site uuid>'
          MELI_TOKEN: ${{ secrets.MELI_TOKEN }}
          GITHUB_BRANCH: ${{ github.payload.pull_request.head.ref }}
pimartin commented 3 years ago

I'd even go further and start building a structure of subcommands, a bit like the docker-cli:

In the end, the CLI could allow users to manipulate most resources (if not all). This would be ideal for automation (CI, scripts, Ansible...).

gempain commented 3 years ago

@queicherius interesting, I haven't used GH Actions enough to know this, but it's perfect for the use case you provided, and keeps us from creating a dependency. I love how you creating you guys can be.

I'm in for the meli site branch delete <branch> and the usual --url ... options. What do you think ?

queicherius commented 3 years ago

I'd happily use that command :) Maybe it'd be a good idea to write out all possible commands up front to create a consistent CLI experience - the command you proposed would work quite different than the current upload one (where branch is required, but provided via a named rather than a positional argument)

gempain commented 3 years ago

Plus I don't know if you've experienced this issue, but when running the CLI in multiline mode, I sometimes get errors when --branch my-branch \ is the last line before the path. I'm not crazy, we've seen this several times but I can't always reproduce.

gempain commented 3 years ago

@queicherius I'll prioritize this feature for the next release as I think this is very important. Not very active today as I was fixing the search on our docs which has been broken for 4 days now as well as doing a bunch of small other stuff 🚀

gempain commented 3 years ago

So I just had a though on this. @queicherius there is a workaround until we implement this feature. You could use the API ! It's a bit tedious but if you really need it urgently this is something that can work. Here's how to do it:

  1. Get an API token from your user settings, and only toggle scopes site.branch.list and site.branch.delete (I like this a lot BTW, being able to discriminate token scopes on specific endpoints).
  2. Get the site via the API
  3. Find the branch ID
  4. Delete the branch via the API

It's doable with CURL but I'm not a bash expert, so here's in JS (I tested this and it works like a charm):

require('dotenv/config');

const url = 'https://meli.site.com';
const siteId = '<your-site-id>';

const axios = require('axios').create({
  baseURL: url,
  headers: {
    'X-Token': process.env.API_TOKEN,
  },
});

async function deleteBranch(name) {
  const { data } = await axios.get(`/api/v1/sites/${siteId}/branches`);

  const branch = data.find(branch => branch.name === name);

  if (!branch) {
    console.log('Branch not found');
    return;
  }

  await axios.delete(`/api/v1/sites/${siteId}/branches/${branch._id}`);
}

deleteBranch('demo').catch(console.error);

One limitation to outline is that the token is not scoped to a specific site. This means technically someone could delete other branches in other sites, but you'd have to know the ID in advance. This wouldn't be an issue with the site token, but right now it can only be used to access the release upload endpoint. I gues this isn't a huge issue if you're the only collaborator in your project or if you trust your collaborators (which I hope you do anyway).

Some improvements we need to make to the API:

Edit: I added some docs here

queicherius commented 3 years ago

Awesome, thank you! 👍