sasjs-cli について
sasjs-cliは、SASjs CLIのコマンドライン操作を説明するスキルリファレンスです。SASプロジェクトのコンパイル、ビルド、テスト、デプロイを行う開発者向けに、コマンドやターゲット設定、認証、CIワークフローを案内しますが、コマンド自体は実行しません。
主な機能
- 01SASjsアプリのひな型作成
- 02SASジョブとサービスのコンパイル
- 03ターゲット向け成果物のビルドとデプロイ
- 04ジョブ、サービス、テスト、フローの実行
- 05ターゲット、認証情報、Viyaコンテキストの管理
- 06ドキュメント生成とSASコードの lint
活用シーン
- 01SASjsプロジェクトをViyaへデプロイする手順の確認
- 02コンパイル、ビルド、デプロイを含むCIの計画
- 03CLIの認証やターゲット設定の問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sasjs/ skills --skill sasjs-cliインストール後、Claude Code で/sasjs-cliを入力して読み込まれたか確認します。
詳細
@sasjs/cli
SASjs CLI(npm i -g @sasjs/cliでインストールし、sasjsとして実行)は、SASプロジェクトのコンパイル、ビルド、デプロイを自動化します。ほとんどのコマンドでは、-t <target>でsasjsconfig.jsonからターゲットを選択します。省略した場合はデフォルトのターゲットが使われます。サーバーにアクセスしないコマンド(lint、db、create、init、doc init)にはターゲットオプションがありません。
@sasjs/cli 6.2.5で確認済みです。フラグはインストール済みCLIの--helpに基づいています。このページに記載されていないフラグを使う前に、sasjs <command> --helpで再確認してください。
ターゲットと認証
- ターゲット =
{ name, serverUrl, serverType, appLoc }です。serverTypeはSAS9、SASVIYA、SASJSのいずれかです。 - 認証情報:
sasjs add cred(またはローカルの環境ファイル)。Viyaではクライアント/シークレット、またはsasjs auth login(ユーザー名/パスワード。クライアント/シークレットは不要です。下記を参照)を使います。SAS 9ではユーザー名/パスワードを、SASJSサーバーではアクセストークンを使います。 sasjs contextではViyaのコンピュートコンテキストを管理し、sasjs add targetでは新しいターゲットを追加します。
基本的なワークフロー
sasjs create myapp # scaffold a new app (templates available)
sasjs compile # gather macros/services/jobs into per-file build outputs (sasjsbuild/)
sasjs compile service -s sasjs/services/public/viewdata.sas # compile one file only
sasjs build # produce deployable artefacts: JSON + .sas per target
sasjs deploy # deploy compiled/built artefacts to the target server
sasjs cbd # compile + build + deploy in one step (-t viya etc.)
単一ファイルのコンパイル
sasjs compileでは、プロジェクトの他のファイルに触れずに、1ファイルずつコンパイルすることもできます。ヘッダーの編集が解決されるかを確認したり、1つのサービスに何が含まれるかを正確に把握したりする最も手早い方法です。
sasjs compile <job|service|test|identify> --source <file.sas> [-o <dir>] [-t <target>]
- サブコマンドにはファイルの種類を指定します。
identifyはファイル自体から種類を判別するため、ファイルがサービスかジョブかわからない場合に安全な選択肢です。 - 出力は種類のフォルダー以下でソースのパスを反映します。たとえば、
sasjs compile identify -s sasjs/services/public/viewdata.sas -o out/を実行すると、out/services/public/viewdata.sasに書き込まれます。-oを指定しない場合は、種類に応じてsasjsbuild/jobsまたはsasjsbuild/servicesに出力されます。 - 例では
-s/--sourceと-o/--outputの両方を示していますが、sasjs compile --helpのOptions:に表示されるのは--targetだけです。これらのオプションは例に記載されているとおりに機能します。 - 全体のコンパイルでは、依存関係のカバレッジ概要(
Services coverage: 26/57 (46%))が出力されます。サービスの大部分がターゲット固有のコピーであるプロジェクトで数値が低くなるのは正常であり、エラーではありません。
コマンドリファレンス
| コマンド | 目的 |
|---|---|
sasjs create / init | 新しいアプリの雛形を作成(--template angular|react|minimal|jobs|sasonly)するか、既存のリポジトリに SASjs を追加します |
sasjs compile [job|service|test|identify] | 依存関係(<h4> SAS Macros </h4> など)を sasjsbuild/ に解決します。サブコマンドと --source を指定すると、単一ファイルをコンパイルします |
sasjs build | コンパイル済みの出力をまとめ、ターゲットごとにデプロイ用の .sas と .json を作成します |
sasjs deploy / cbd | サーバーにデプロイします(servicepack または直接)。cb はコンパイル+ビルドです |
sasjs run <file.sas> | 任意の SAS ファイルをサーバーで実行し、ログを返します。-c は先にコンパイルし、-s はジョブ変数を JSON で指定し、-l はログを保存します |
sasjs request <path> | 入力データを使ってデプロイ済みのサービス/ジョブを実行します。-d はデータ JSON、-c は設定 JSON、-l はログ(待機を伴います)、-o は出力 JSON を指定します |
sasjs job execute <jobPath> | デプロイ済みのジョブを実行します。-w は待機、-l はログ、-o は出力、-i は警告を無視、-v は詳細表示を指定します |
sasjs flow execute | 依存関係のある一連のジョブを実行します。-s はフロー JSON、-l はログフォルダー、-c は CSV、--streamLog を指定します |
sasjs servicepack deploy | JSON サービスパックからデプロイします(-s、上書きする場合は -f) |
sasjs context create/edit/delete/list/export | Viya のコンピューティングコンテキストを管理します |
sasjs web | フロントエンドをビルドし、SAS のウェブルートにストリーミングします(streamConfig) |
sasjs db | sasjs/db 内の DDL/データスクリプトを連結し、ビルド出力にまとめます |
sasjs doc [init|lineage] | Doxygen ドキュメント、またはリネージの .dot ファイルを生成します(-o は出力ディレクトリ) |
sasjs snippets | プロジェクトのマクロの Doxygen ヘッダーから VS Code スニペットを生成します |
sasjs folder create/delete/list/move | SAS メタデータ/SAS Drive 内のフォルダーを管理します(create -f は強制的に再作成します) |
sasjs fs sync / sasjs fs compile | syncDirectories に従ってローカルとリモートのフォルダーを同期します。リモートフォルダーを単一のプログラムにコンパイルするには -o を指定します |
sasjs test [filters..] | sasjs/tests で定義されたテストを init/term プログラムとともに実行します。-s はテストフロー、--out は結果ディレクトリ、-f はパイプラインを失敗させないようにします |
sasjs lint [init|fix|find] | .sasjslint に従って .sas ファイルをリントします。引数なしの sasjs lint は find として動作します。詳細は sasjs-lint スキルを参照してください |
sasjs version | バージョン情報を表示/設定します |
エイリアス:b build、c compile、cb compilebuild、cbd compilebuilddeploy、d deploy、docs doc、h help、rq request、r run、v version、w web、build-db/DB db。
規約
-
テストカバレッジは、
sasjs compile(またはsasjs c)からのみ生成されます。ターゲット(-t <target>)を指定できますが、そのターゲットには何もデプロイされません。コンパイルとカバレッジ生成は完全にローカル/オフラインで行われるため、サーバーが利用可能である必要も、接続可能である必要もありません。CLI には独自の@sasjs/core依存関係が含まれているため、@sasjs/coreが提供するマクロ(例:mp_ds2csv.sas)は、プロジェクトにnode_modulesがなくても解決されます。両方に存在しないマクロは、実際に不足している依存関係です。 -
依存関係は、doxygen ヘッダーの
<h4> SAS Macros </h4>、<h4> SAS Files </h4>、<h4> SAS Folders </h4>、および@li itemエントリーで宣言します。CLI はこれらを基に依存関係ツリーを構築します。 -
@liリストはコンパイラーが参照する唯一の依存関係情報源です。コード内で呼び出されていても宣言されていないマクロはビルドにインライン化されず、デプロイされたジョブは実行時にApparent invocation of macro X not resolvedで失敗します。別の宣言済みマクロの@liチェーンによって偶然インライン化され、動作する場合もあります(推移的な解決)。この方法は、そのマクロの依存関係が変わるとすぐに動作しなくなります。間接的に呼び出されるヘルパー(例:mf_getuser.sas)も含め、コードから呼び出すすべてのマクロを宣言してください。既存のサービスを編集するときは、本文内の%macro()呼び出しを@liエントリーと照合し、改めて確認してください。 -
sasjs compileの出力先はsasjsbuild/フォルダーです(gitignore に追加してください)。テスト/実行結果はsasjsresults/に保存されます。 -
CI/CD:
sasjs cbd -t viyaが標準のデプロイ手順です。成果物ベースのリリースではsasjs servicepack deployと組み合わせてください。 -
失敗時の終了コードはゼロ以外です。パイプラインで安全に使用できます。
注意点
sasjs compileの実行にnpm iは必須ではありません。 CLI 自体が@sasjs/coreに依存しており、コアマクロを独自のコピーから解決するため、node_modulesのない新しいクローンやワークツリーでもコンパイルできます(6.2.5 で確認済み:依存関係のないワークツリーでプロジェクト全体をコンパイルし、生成されたサービスにコアマクロが含まれていました)。それでも、ビルドとテストで使用するバージョンを明示し、インストール済み CLI に同梱されるバージョンに依存せずロックファイルで固定するため、@sasjs/core(使用する場合は@sasjs/adapterも)をpackage.jsonに含めてください。streamWebのコンパイルでは、ERROR がログに出ても終了コードが 0 の場合があります。streamConfig.streamWeb: trueのとき、webSourcePath(通常はフロントエンドのビルド前には存在しないclient/dist)が見つからないと、ERROR An error has occurred when compiling web app services. Error: webSourcePath: '...' doesn't exist.と表示された後にThe project was successfully compiled ...と表示されます。終了コードだけを基準にするパイプラインのステップは、ウェブアプリがビルドされていないにもかかわらず成功してしまうため、このメッセージがログにないか確認するか、コンパイル前にフロントエンドをビルドしてください。- 警告だけの場合、
sasjs lintの終了コードは 0 です。 また、--helpではサブコマンドが必須と表示されますが、引数なしのsasjs lintはsasjs lint findとして動作します。ゲートとして使用するには、severityLevelによって必要なルールの重要度を引き上げてください(noEncodedPasswords以外のすべてのルールの既定値は"warn"です)。ルールと設定の詳細はsasjs-lintスキルを参照してください。 sasjs folder --helpでは選択肢としてcreate、delete、listが表示されますが、トップレベルのヘルプにはmoveも記載されています。 スクリプトで使用する前に、インストール済みバージョンでsasjs folder move --helpを確認してください。- 認証情報ファイルはターゲットごとに用意します。
.env.<targetname>(例:.env.server)という名前の環境ファイルに、CLIENT、ACCESS_TOKEN、REFRESH_TOKENの各エントリーを記述します。決してコミットしないでください。.env*を gitignore に追加してください。
クライアント/シークレットなしの Viya 認証(sasjs auth login)
sasjs auth login -t <target> は、組み込みのシークレット不要な sas.cli パブリッククライアントに対して OAuth2 パスワードグラントを使用し、通常の SAS ユーザー名とパスワードで認証します。管理者が OAuth クライアントを登録する必要がなく、開発/デモ環境で sasjs run/deploy を使えるようにする最速の方法です。パスワードは保存されません。発行されたトークンペアはローカルの環境ファイル(.env.<target>)または ~/.sasjsrc(グローバル)に保存され、/identities/users/@currentUser を通じて検証されます(Logged in as <id> (<name>))。引数なしの sasjs auth は引き続き sasjs add cred のエイリアスです。
- トークンの有効期限:CLI は保存されたリフレッシュトークンでサイレントに更新し(クライアント/シークレットの有無を問いません)、ローテーションされたペアを再保存します。Viya のリフレッシュトークンは単回使用でローテーションされるため、以降の呼び出しを機能させるには、この再保存が必要です。更新に失敗した場合は、
sasjs auth loginを再実行してください。 - 一部の環境では、
sas.cliのアクセストークン有効期間が短く(例:1h)、ほとんどの呼び出しで更新されるのは通常の動作です。 - 不透明な(非 JWT)トークンは使用可能として扱われます。有効期限の判定はサーバーが行います。
- 制限事項:ローカル/LDAP アカウントのみ(SSO/SAML/MFA を使用する環境は対象外)。
sas.cliでパスワードグラントが有効になっている必要があります(Viya 3.5+/4 ではデフォルトで有効)。ROPC は OAuth 2.1 で非推奨です。CI/本番環境では登録済みのクライアント/シークレットを使用してください。 - セッション作成時に
sasjs runが 403 を返す場合、そのアカウントには設定されたコンピュートコンテキストの使用権限がありません。ターゲットにcontextName: "SAS Studio compute context"を設定してください。コールド状態の環境での初回実行には(コンピュートポッドの起動に)数分かかることがあり、ハングしているように見える場合があります。 - 自己署名証明書を使用する環境では、
auth loginに--insecureを指定するか、ターゲットにhttpsAgentOptionsを設定してください。
非対話型認証(CI とエージェント)
sasjs auth login でプロンプトを表示する必要はありません。環境変数が設定されている場合は SAS_USERNAME と SAS_PASSWORD を読み取り、--passwordStdin を指定すると標準入力からパスワードを読み取ります。
SAS_USERNAME=user SAS_PASSWORD=secret sasjs auth login -t <target>
echo "$SAS_PASSWORD" | sasjs auth login --password-stdin -t <target>
--password-stdin を使うと、プロセス一覧やシェル履歴にパスワードが残らないため、シークレットストアから値を取得する場合は常にこちらを使用してください。どちらも指定しない場合、CLI はプロンプトを表示します。その場合、端末からの入力を待つため、無人実行のジョブはハングします。
制限事項
このスキルは SASjs CLI の静的なリファレンスであり、sasjs コマンドラインツールの使用方法を案内します。CLI コマンドの実行、ファイルシステムへのアクセス、認証情報の読み取り、ネットワークリクエストの送信は行いません。記載されているコマンドはすべて例示です。ユーザー自身の環境で実行する必要があります。認証情報ファイル(.env、~/.sasjsrc)への言及は、CLI が認証トークンを保存する場所を示すものであり、このスキル自体がそれらのファイルを読み取り、書き込み、またはアクセスすることはありません。
Viya ストリーミングアプリ(streamConfig.streamWeb)
streamWeb: true の場合、sasjs web/cbd -t viya はフロントエンドをSAS Files Service 内にデプロイします。<appLoc>/services/<streamServiceName>.html にあるストリーミングジョブが index.html を配信し、アセットは <appLoc>/services/web/... に配置されます。ビルド時に HTML 内のすべてのアセット/スクリプト/CSS の URL が /SASJobExecution?_FILE=<appLoc>/services/web/... に書き換えられ、アダプター設定要素(<sasjs apploc=...>)にはターゲットの appLoc が設定されます。
そのため、アプリはデプロイ先とまったく同じ appLoc から配信された場合にのみ動作します。 ストリーミング HTML を別の場所(例:/Users/<id>/myapp/... のようなユーザーのホームフォルダーに手動でアップロードした場所)に配置すると、書き換えられた /Public/app/... アセットリンクはすべて 404 になり、アダプターは誤ったサービスパスを呼び出します。その結果、ページは CSS/JS もバックエンドもない状態で表示されます。デプロイし直して解決してください。元のビルド(sasjs cb)には sasjsconfig.json の apploc が設定されています。アプリを SAS プログラムとしてデプロイする際、指定された %let apploc = (実行時の値)は compiled_apploc(ビルド時の値)に置き換えられ、デプロイ時にアプリを任意の apploc に動的にデプロイできます。
Viya のデプロイをブラウザーなしで検証する
- トークンを取得します。認証情報を直接扱うより、CLI の利用を優先してください。
sasjs auth login -t <target>を実行すると、OAuth フローとトークンの保存が処理されます。curl 用にトークンが必要な場合は、パブリックなシークレット不要のクライアント(sas.ec)から、/SASLogon/oauth/tokenの OAuth パスワードグラントを使ってトークンを発行してください。SAS Viya REST の認証ドキュメントを参照してください。スクリプトやコマンドラインに実際の認証情報を埋め込まないでください。 - アプリを取得します。
GET /SASJobExecution/?_FILE=<appLoc>/services/<name>.htmlにAuthorization: Bearerを付けてリクエストすると、200と完全なindex.htmlが返るはずです。401は認証エラーで、それ以外はその場所にデプロイされていないことを意味します。 - HTML が参照する各アセットを取得します。レスポンスから問題の内容がわかります。
- ファイルの内容とともに
200が返る場合、アセットは正しくデプロイされています。 - 小さなプレーンテキストの本文(例:
Parameter Error\nFile error)とともに202が返る場合、その Drive パスにファイルが存在しません(appLocの不一致でよく見られる症状です)。_FILEのレスポンスは非同期の場合があるため、コンテンツを同期的に取得するには&_action=waitを追加してください。 - サービスの場合:
POST /SASJobExecution/?_program=<appLoc>/services/<svc>&_action=waitでParameter Error / Unable to get job definitionが返る場合、そのサービスは JES ジョブとしてデプロイされていません(ファイルを Drive に置くだけでは不十分です。sasjs deploy/cbd/servicepack deployのみが登録を行います)。
- ファイルの内容とともに
SAS との通信には、必ずアダプターまたは CLI を使う
デバッグ、再現、CI など、どのような目的であっても SAS サービスまたはジョブを実行する場合は、必ず @sasjs/adapter または sasjs CLI を使用してください。SASJobExecution に対して curl を手作業で組み立てないでください。アダプターと CLI は、手作業では間違いやすい以下の処理を行います。入力データの CSV 形式(スペース区切りの name:$format. ヘッダー、CRLF、特殊値の二重引用符での囲み、%nrstr(...) によるラップ)、実行モードのルーティング(_executionTasks=true では、sasjs<N>data はファイルアップロードではなくマクロ変数として読み込まれます)、_debug/>>weboutBEGIN<</>>weboutEND<< のラッパー解析、トークンの更新、_contextname URL パラメーターです。手作業で作成したカンマ区切りの CSV は空欄として読み込まれ、ガードされたマクロブロックが黙ってスキップされるため、存在しないバグの調査に時間を費やすことになります。
- 入力データを JSON ファイルとして作成します。
{ "<table>": [{ "<col>": "<val>", ... }] }。例:{ "config": [{ "rootdir": "/export/...", "runastask": "true", "usecomputeapi": "null", "contextname": "Compute Reusable" }] }。 - 次を実行します。
sasjs request '<appLoc>/services/common/<svc>' -t viya -d data.json -l <path>.log -o <path>.json。CLI はdebug: trueをハードコードしているため、-lフラグを使うと、MPRINT/NOTE/&sysccのログ全体が常に保存されます。-oは解析済みの webout を保存します。必ず-lを指定してください。ログなしでバグを再現すると、処理全体をもう一度実行することになります。 - Viya Folders/JES REST API は、フォルダー/ファイル/メンバーの管理や、アセットを確認するための
GET /SASJobExecution/?_FILE=...であれば、トークンとAuthorization: Bearerを付けて curl から直接呼び出しても問題ありません。ただし、入力データを使って独自のサービスを実行する場合は使わないでください。
アダプターの実行コンテキストと入力データに関する事実
- アダプターは、すべての Viya JES リクエストに
_contextname=<value>をURL パラメーターとして自動的に追加します(自分で渡す必要はありません)。ただし、JES リクエストのパラメーターはSAS マクロ変数に自動的に昇格されません。サービス内では%symexist(_contextname)は false になります。選択したコンテキスト名がサービスで必要な場合(たとえば、ストリーミングする HTML に埋め込む場合)は、入力データテーブル(contextname列)で渡し、call symputxで読み取ってください。これが信頼できる受け渡し方法です。 sasjsconfig.jsonのターゲットのcontextNameによって、サービスを実行するコンピュートコンテキスト(ひいてはrunAsの ID)が決まります。アダプターの URL の_contextnameパラメーターには同じ値が設定されます。sasjs requestを使って、バッチ/再利用可能コンテキストでサービスを再現するには、ターゲットのcontextNameをその再利用可能なコンテキスト(たとえばCompute Reusable、runAs=sasbatch)に設定してください。そうしないと、自分自身の ID で実行され、バッチ所有フォルダーに対する書き込みテストの手順がUser does not have appropriate authorization levelで失敗します。- アダプターの CSV 形式(ログの
NOTE: The infile ... is:のルールを正しく読み取るための情報):1 行目はname:$informat.の組をスペース区切りにしたヘッダーです。データ行はカンマ区切り(CRLF)で、特殊文字(,,", タブ、改行)を含む値のみ二重引用符で囲まれます。sasjs/core の webout リーダーは、ヘッダーから生成したinput <name>:$informat.;ステートメントを使い、dsdとfirstobs=2を指定して読み戻します。 _executionTasks=true(runAsTask)にすると、入力データの受け渡し方法が変わります。_WEBIN_FILEアップロードではなく、sasjs<N>dataマクロ変数(sasjs<N>data0..Nに分割)として渡されます。core webout のmv_weboutマクロはどちらにも対応しますが、_EXECUTIONTASKSに応じて処理が分岐します。ログを読む際はこの点に注意してください。
Viya へのクリーンな再デプロイ(409 Conflict の問題)
既存の appLoc に対して sasjs cbd/sasjs run viya.sas を再実行すると、デプロイの途中で 409 Conflict(および mp_abort -> abort cancel)が発生して失敗することがよくあります。mv_createfile は古いファイル ID を DELETE してから再作成しようとしますが、中間のフォルダー(たとえば <appLoc>/services/web/js)がすでに存在すると、再作成処理が競合してデプロイ全体が中断され、アプリが不完全な状態で残ります(サービスは存在するものの、一部のアセットが欠落します)。?recursive=true を指定したフォルダーの DELETE も 409 You cannot delete the folder because it is not empty を返します。また、個々のメンバー/フォルダーの DELETE は所有者であっても 403 を返す場合があるため、ツリーを手作業で簡単に削除することはできません。
確実な回避策は、最上位の appLoc フォルダーを別の場所に移動してから元のパスに再デプロイすることです。デプロイ時に競合のない新しいフォルダーツリーが作成され、古いフォルダーはバックアップとして保持されます。これはメンバーごとの削除に手間をかけるよりもはるかに速く、信頼性も高いため、Viya で CI 以外の再デプロイを行う場合に推奨される事前手順です。移動方法は 2 つあります。
- 同じ場所で名前を変更する(最も簡単):
PATCH /folders/folders/{id}に{"name":"<old>.bak.<ts>","version":2}を指定します。元の名前が空き、古いフォルダーは同じ階層にバックアップとして残ります。 - バックアップ用の親フォルダーに MOVE する(より整理された方法。すべてのバックアップを 1 か所にまとめられます):まず、たとえば
/Users/<id>/macrodash-backupsのようなバックアップ用の親フォルダーを作成します。その後、POST /folders/folders/{backupParentId}/members/{appLocFolderId}?action=moveを実行すると、コンテンツを含むツリー全体がそこに移動し、新規デプロイ用に元のパスが空きます。
(sasjs/server では、まず sasjs fs delete で appLoc を削除するだけです。そこでは Drive が完全な再帰削除をサポートしています。)
JES はストリーミング時に独自の CSP ヘッダーを適用します(unsafe-inline/unsafe-eval を含みます)。アプリの HTML に厳格な CSP メタタグがある場合も引き続き適用され、アプリのコードに影響するのはこちらです。