Github Package Registry にpushしたイメージを削除する方法

# 結論
結論から先に書くと public リポジトリの場合は一度 push したイメージは自分では削除できない！
どうしてもって場合は [ここ書いてあるフォーム](https://help.github.com/articles/about-github-package-registry/#deleting-a-package) から中の人にお願いするしかないようだ…。

とりあえずお願いはした。結果は後日追記予定。
↓
フォームのお願いから約1週間。削除したよって連絡きてた！！
<img width="509" alt="スクリーンショット 2019-10-30 15.19.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18726/40240fea-c237-fdcb-f236-b94c5257fc0d.png">
ちゃんと[消えてた](https://github.com/kawaz/docker-ansible/packages/41437)！

# 始まり
Github Package Registry がいつの間にか使えるようになってたので早速適当なイメージを push してみるぜ！
↓
出来たー！
ん？[ansib<span>***k***</span>e](https://github.com/kawaz/docker-ansible/packages/41437)？

<img width="1010" alt="スクリーンショット 2019-10-22 16.37.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18726/00823fb5-b5a8-b073-ba92-ecc73c35cd47.png">

ああ…あああー、typo してるーーーー！！
とりあえず削除だー！ってこれどうやって削除すればいいんだ？ってことで削除しようと思ったがどうにも削除的なメニューが見つからないし。docker のサブコマンドにもそれらしいのはないし、あれ？どうすればいいんだ？って事で調べ始めたら以外にも大変だった。

# GraphQL で頑張ってみた軌跡…

とりあえず使うAPIは Github GraphQL で [deletePackageVersion](https://developer.github.com/v4/mutation/deletepackageversion/) という mutation を使えば良さそう。

しかしどうやって叩けばよいか分からないのでググる…。そしてドキュメントの input や returns フィールドの仕様を参照しつつ以下のようなクエリを投げればよさそうだということが分かる。

```bash
curl -sL -XPOST https://api.github.com/graphql \
  -H "Authorization: bearer $TOKEN" \
  -H 'Accept: application/vnd.github.package-deletes-preview+json' \
  -d '{"query":"mutation{deletePackageVersion(input:{clientMutationId: \"1\",packageVersionId:\"XXXXX\"}){clientMutationId,success}}"}'
```

分かりにくいので書き下すと、POSTしたJSONのqueryの値とGraphQLとレスポンスは以下のようになっている。
inputに指定する値が何なのかよくわからなかったので適当に入力しているので怒られている。ちなみに `clientMutationId` は何でも良さそう(多分リクエストとレスポンスの紐付け用に利用者的に一意な値を渡せる奴で別に同じでも動く)だが、`packageVersionId` に指定すべき値が何なのかよく分からない…とりあえず適当に投げてみる。

```jsonnet
mutation {
  deletePackageVersion(
    input:{
      clientMutationId: "1",
      packageVersionId: "XXXXX"
    }
  )
  {
    clientMutationId,
    success
  }
}
```

↑GraphQLのクエリ　↓レスポンス

```json
{
  "data": {
    "deletePackageVersion": null
  },
  "errors": [
    {
      "type": "NOT_FOUND",
      "path": [
        "deletePackageVersion"
      ],
      "locations": [
        {
          "line": 1,
          "column": 10
        }
      ],
      "message": "Could not resolve to a node with the global id of 'XXXXX'"
    }
  ]
}
```

とりあえずこの意味が分かる `message` をもらうまでがまず大変だった…。気を取り直して `packageVersionId` を考える。とりあえず `latest` とか `ansibke:latest` とか `kawaz/docker-ansible/ansibke:latest` とか思いつく限りの値を入れてみるがどれも 404 が返ってくる…。
改めて `packageVersionId` の型である [ID](https://developer.github.com/v4/scalar/id/) のドキュメントを見てみると、base64エンコードされたユニークな値らしい。そりゃわからんわ。ていうかこれはまず GET 系のクエリを投げて内部IDみたいなのを探す必要があるんじゃね？てことでパッケージ一覧を取得するAPIを探す旅に出る。

1時間位ドキュメント読んだりAPI試したりしながら漸く分かった…てか GraphQL 大変すぎだろ。レスポンスに欲しいフィールドを全部調べて列挙しといてやらないといかんとか大変すぎ…。てことでドーン。

```bash
curl -sL -X POST https://api.github.com/graphql \
  -H "Authorization: bearer $TOKEN" \
  -d '{"query":"query{repository(owner:\"kawaz\",name:\"docker-ansible\"){registryPackages(first:10){nodes{packageType,registryPackageType,name,nameWithOwner,
id,versions(first:10){nodes{id,version,readme}}}}}}"}'
```

```jsonnet
query {
  repository(owner:"kawaz", name:"docker-ansible") {
    registryPackages(first:10) {
      nodes {
        packageType,
        registryPackageType,
        name,
        nameWithOwner,
        id,
        versions(first:10) {
          nodes{
            id,
            version,
            readme
          }
        }
      }
    }
  }
}
```

↑GraphQLのクエリ ↓レスポンス

```json
{
  "data": {
    "repository": {
      "registryPackages": {
        "nodes": [
          {
            "packageType": "DOCKER",
            "registryPackageType": "docker",
            "name": "ansibke",
            "nameWithOwner": "kawaz/ansibke",
            "id": "MDE1OlJlZ2lzdHJ5UGFja2FnZTQxNDM3",
            "versions": {
              "nodes": [
                {
                  "id": "MDIyOlJlZ2lzdHJ5UGFja2FnZVZlcnNpb24xNzc3MTY=",
                  "version": "docker-base-layer",
                  "readme": "あああ。。。イメージ名 typo しとる…。"
                }
              ]
            }
          },
          {
            "packageType": "DOCKER",
            "registryPackageType": "docker",
            "name": "ansible",
            "nameWithOwner": "kawaz/ansible",
            "id": "MDE1OlJlZ2lzdHJ5UGFja2FnZTQxNDQw",
            "versions": {
              "nodes": [
                {
                  "id": "MDIyOlJlZ2lzdHJ5UGFja2FnZVZlcnNpb24xNzc3MzA=",
                  "version": "latest",
                  "readme": null
                },
                {
                  "id": "MDIyOlJlZ2lzdHJ5UGFja2FnZVZlcnNpb24xNzc3Mjk=",
                  "version": "2.8.4",
                  "readme": null
                },
                {
                  "id": "MDIyOlJlZ2lzdHJ5UGFja2FnZVZlcnNpb24xNzc3Mjc=",
                  "version": "docker-base-layer",
                  "readme": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

あーもー、GraphQL面倒くせーー！！てことでやっと欲しかった ID っぽいものが出てきた。ので最初の削除クエリにぶっこんで見る！


```bash
curl -sL -XPOST https://api.github.com/graphql \
  -H "Authorization: bearer $TOKEN" \
  -H 'Accept: application/vnd.github.package-deletes-preview+json' \
  -d '{"query":"mutation{deletePackageVersion(input:{clientMutationId: \"1\",packageVersionId:\"MDIyOlJlZ2lzdHJ5UGFja2FnZVZlcnNpb24xNzc3MTY=\"}){clientMutationId,success}}"}'
```

で、遂に削除クエリのレスポンスの形が変わった！コレは勝つる！？？？　…ええええええー。

```json
{
  "data": {
    "deletePackageVersion": null
  },
  "errors": [
    {
      "type": "UNPROCESSABLE",
      "path": [
        "deletePackageVersion"
      ],
      "locations": [
        {
          "line": 1,
          "column": 11
        }
      ],
      "message": "Public package versions are not eligible for deletion. For more on our deletion policy, see https://help.github.com/articles/about-github-package-registry/#deleting-a-package."
    }
  ]
}
```

要約「パブリックパッケージはポリシー的に削除しない事にしてるんで、どうしてもっていうなら[ここ](https://help.github.com/articles/about-github-package-registry/#deleting-a-package)から連絡してね」ってことで散々苦労したけど、結論は削除出来ないでした！！

まぁ、今回の作業で GraphQL の感覚が何となく理解できたので良しとしよう…。