sfdxコマンドを利用して、Salesforceの本番組織からApexクラスファイルを削除する


『Salesforceの本番組織からApexクラスを、もっと！もっと！楽に削除したい！！！』ってお話です。

## ことの発端

開発用のDE組織やSandbox組織と比べて、Salesforceの本番組織からApexクラスファイルを削除する時、公式サイトをみるとAnt 移行ツールを使った例が出てきたり、検索するとJSforce Metadata Toolsを利用する例やらWorkbenchを利用する例やら色々と出てきます。

本番組織からApexクラスファイルを削除するという作業は、基本的にやりたくない行為ではありますが、パッケージをインストールした時に、そのパッケージが自動生成したApexクラスなどが邪魔して、アンインストールするときに指定のApexクラスを削除しなければアンインストールできません！！って言うのですよね。


私には、<font color="Red">（わざわざ追加でツールをインストールするしか方法がないのか・・？）</font>と感じる方法ばかり目についてしまい、諦めてAnt移行ツールを使う方針で作業進めていたところ、颯爽とやってきた上司に『今はSFDXコマンドで対応できるようになっているよ！調べてみたら？』という、ありがたい助言を受けました。

その後、私自身が<font color="Blue">（アァ。。。コレ楽ダ。。）</font>と感じた`sfdx force:mdapi:deploy`コマンドを利用する方法を忘れないようにここに残します。

## 前提
普段からSFDXコマンドを利用した開発をしている人向けとなっております。

:::note warn
（※注意※SFDXコマンドは、Salesforce CLIツールである）
この環境がない人は『結局ツールを追加でインストールするの必要じゃねーか！！』となると思います。

せめて、Salesforce CLIツールの使い方だけ覚えるだけでも良いかもしれないです。
[Salesforce CLI 設定ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm)を参考にSalesforce CLI をインストールされてください。
```
// 以下コマンドが実行できる環境が必要です。
sfdx --version
```

■VSCodeを使った環境構築をしたい人は...
VSCodeでSFDXコマンドを使った環境構築は、Trailheadのプロジェクト「[クイックスタート: Salesforce 開発のための Visual Studio Code](https://trailhead.salesforce.com/ja/content/learn/projects/quickstart-vscode-salesforce)」 や @shunkosa さんの書いたQiita記事[Visual Studio Code で Salesforce の開発環境を構築する](https://qiita.com/shunkosa/items/e6248520bff90f397158)が非常に参考になります。

:::

## やり方
ひとことで言うと`destructiveChanges.xml` という名前の削除マニフェストファイルを事前に定義し`sfdx force:mdapi:deploy`コマンドを実行するだけです。

### 1.以下のようにフォルダを1つ作成し、その中に以下2つのファイルを作成します
```
removepkg // 任意のフォルダ
   ├── destructiveChanges.xml   // XMLファイル
   └── package.xml    // XMLファイル
```


```removepkg/package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <version>54.0</version>
</Package>
```

```removepkg/destructiveChanges.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
      <members>削除したいApexクラス名1</members>
      <members>削除したいApexクラス名2</members>
      <name>ApexClass</name>
    </types>
    <version>54.0</version>
</Package>
```

:::note info
※このファイルの意味や書き方は、以下を参考にしてください。
[組織からのファイルの削除 | Ant 移行ツールガイド | Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.daas.meta/daas/daas_destructive_changes.htm)

＞なぜ？Ant 移行ツールガイドなの？
ローカルPC上に存在するXML メタデータファイル（指令書みたいなもの）をSalesforce組織にアップロードして実行させるツールです。これと同じことはSFDXコマンドの`sfdx force:mdapi:deploy`でも対応できます。メタデータファイルの書き方として仕組みが同じであるため、Ant 移行ツールガイドのリンクを紹介しました。

＞他の書き方もあるの？
**SFDXプロジェクト上で** SFDXコマンドを実行する環境が整っているのであれば、コマンド「`sfdx force:source:manifest:create -m ApexClass:削除したいApexクラス名 --manifesttype destroy`」実行でも自動生成できます。
:::


### 2.`sfdx force:mdapi:deploy`コマンドを実行してファイルを削除します
```
sfdx force:mdapi:deploy --deploydir removepkg \
--targetusername <Salesforce組織へのログイン可能なユーザIDもしくはエイリアス> \
--testlevel RunLocalTests \
-w 5
```

 - 引数1つ目の`--deploydir`では実行するメタデータファイルが存在しているフォルダを指定します。
ここでは事前準備で作成した`removepkg`を指定してます。この名前は任意に変えること可能です。

 - 引数2つ目の`--targetusername`は組織へログイン可能な権限を持つユーザーのIDもしくは事前に名前付きで認証をしているならば組織のエイリアス名を指定します。
 
 - 引数3つ目の`--testlevel`では、組織に情報をデプロイ時のテストを実行するかどうかのフラグです。本番組織から直接Apexクラスファイルを削除する際は、最低でもローカルテストの実行必須です。`NoTestRun`を指定すると、エラーメッセージ`ERROR running force:mdapi:deploy:  INVALID_OPERATION: testLevel of NoTestRun cannot be used in production organizations`が発生するので`RunLocalTests`を指定しましょう。
 
- 引数4つ目の`-w`はコマンドの実行待機時間（分）です。今回は5分と指定しておりますが、テスト実行に時間がかかるような組織の場合は、それに合わせて長めに時間を指定しましょう。

- `\`は長いコマンドを入力する際に改行して見やすくするために利用しているので、以下のように一行で実行しても問題ないです。(Linux、Mac系のターミナルの文化かも)
  -  `sfdx force:mdapi:deploy --deploydir removepkg --targetusername <Salesforce組織へのログイン可能なユーザIDもしくはエイリアス> --testlevel RunLocalTests -w 5`

:::note info
`sfdx force:mdapi:deploy`コマンドの詳細は以下公式サイトのリファレンスをご確認ください。
[mdapi Commands | Salesforce CLI Command Reference | Salesforce Developers](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_mdapi.htm#cli_reference_force_mdapi_deploy)
:::

### 以上！！


## （余談）その他にも...※加筆※

SFDXコマンドを使ったApexクラスファイルの削除は、他にも`sfdx force:source:retrieve`、`sfdx force:source:deploy`を使う方法があります。

このやり方の大前提として、SFDXプロジェクト上で SFDXコマンドを実行する環境が整っている必要があります。
もし、SFDXプロジェクトが存在しない場合は、先に以下のコマンドを実行して環境をローカル上に作ってください。
```
// SFDXプロジェクトを作成
sfdx force:project:create --projectname <SFDXプロジェクト名（フォルダ名)>

// 制御したいSalesforce組織にログインしておく
sfdx auth:web:login -a <ログインしたSalesforce組織に別名付与>
```

<details><summary>(詳細みたければ、ここ展開してね)`sfdx force:source:retrieve`や`sfdx force:source:deploy`を使う具体的なやり方は以下の通りです。</summary>

まず`sfdx force:source:retrieve`コマンドを実行して、ローカル環境にApexクラスファイルを落としてくる。
```
// 実行するコマンド
sfdx force:source:retrieve -m "ApexClass:削除したいApexクラスファイル" -u <Salesforce組織へのログイン可能なユーザIDもしくはエイリアス>
// コマンド結果
Preparing retrieve request... done
=== Retrieved Source
FULL NAME           TYPE       PROJECT PATH
──────────────────  ─────────  ──────────────────────────────────────────────────────────────
SVE_20150409121815  ApexClass  force-app/main/default/classes/削除したいApexクラスファイル.cls
SVE_20150409121815  ApexClass  force-app/main/default/classes/削除したいApexクラスファイル.cls-meta.xml
```

ローカルに落ちてきたApexクラスファイルの`.cls-meta.xml`ファイルを開き、`<status>Active</status>`を`<status>Deleted</status>`に変更。

そして、`sfdx force:source:deploy`コマンドを実行すると、ファイルが削除されます。
```
// 実行するコマンド
sfdx force:source:deploy -p "force-app/main/default/classes/削除したいApexクラスファイル.cls" -l RunLocalTests -u <Salesforce組織へのログイン可能なユーザIDもしくはエイリアス>
// コマンド実行結果
Deploy ID: XXXXXXX

=== Deployed Source
FULL NAME           TYPE       PROJECT PATH
──────────────────  ─────────  ──────────────────────────────────────────────────────────────
SVE_20150409121815  ApexClass  force-app/main/default/classes/削除したいApexクラスファイル.cls
SVE_20150409121815  ApexClass  force-app/main/default/classes/削除したいApexクラスファイル.cls-meta.xml

=== Test Results Summary
Passing: NN
Failing: 0
Total: NN
Time: 0
```
</details>

SFDXプロジェクトが存在しており、ソース管理しているなら、こちらの方法が早いかもしれません。


ただし、こちらの方法は依存しているファイルが存在していてもエラーメッセージが表示されず処理は成功とレスポンスが返ってきます。
そのためコマンドは成功しても、削除できないという現象が発生してしまい、原因分からずジレンマに陥りました。

<font color="Blue">記事公開後、さらに助言をうけ追加で分かったことがありましたので以下加筆しました。</font>

そのほか、`sfdx force:source:deploy`に引数`--postdestructivechanges`を付与して削除する方法があります。

<details><summary>(詳細みたければ、ここ展開してね)`sfdx force:source:deploy --postdestructivechanges`を使う具体的なやり方は以下の通りです。</summary>

まず、以下を実行して削除用のマニフェストファイル（=destructiveChanges.xml)を自動生成します。

```
sfdx force:source:manifest:create -m ApexClass:削除したいApexクラス名 --manifesttype destroy
```
この時、package.xmlは自動生成されないので、上記で紹介しいるのと同じように自作します。

```package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <version>54.0</version>
</Package>
```

そして以下コマンドを実行します。

```
sfdx force:source:deploy --manifest package.xml --postdestructivechanges destructiveChanges.xml -l RunLocalTests -u <Salesforce組織へのログイン可能なユーザIDもしくはエイリアス>
```

:::note info
postdestructivechanges引数を利用するときは、manifest引数必須です。
詳細は、SFDXコマンドの`--help`を利用しつつ、公式サイトを確認しながら実行してください。
`sfdx force:source:deploy`コマンドの詳細は以下公式サイトのリファレンスをご確認ください。
[source Commands | Salesforce CLI Command Reference | Salesforce Developers](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_force_source.htm#cli_reference_force_source_deploy)
:::

</details>

こちらの方法は、SFDXプロジェクトが存在しており、ソース管理しているなら、上記の`sfdx force:source:retrieve`が不要なので、さらに楽できます！
なお、依存関係ある場合、`Deploy failed.`とレスポンスがあり、失敗していることが分かりやすいです。
　...しかし、大変残念な事に何が原因でエラーが発生しているのか等の詳細な情報は何故か出力されません。
そのためコマンドは失敗して削除できない。かつ失敗原因は分からず。という動きをする事がわかりました。

この引数`postdestructivechanges`が提供されたのは、つい最近のv53(Winter '22)のリリースです。
よって、まだまだ発展途上なのかもしれません。今後に期待しつつ、いつかのリリースで改善されるかもしれませんね。

以上のことから、原因不明のエラー発生時は`sfdx force:mdapi:deploy`コマンドを利用するのが当面のお勧めかもしれません。

## まとめ
以上！！！
普段からSFDXコマンドに慣れ親しんでいる人向けの、おすすめ紹介でした(*´ `)
ではでは。