@microsoft/managed-apps-cli 0.13.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/ArgumentProvider.d.ts +1 -2
- package/dist/ArgumentProvider.d.ts.map +1 -1
- package/dist/ArgumentProvider.js.map +1 -1
- package/dist/Authentication/NoOpAuthenticationProvider.d.ts +25 -0
- package/dist/Authentication/NoOpAuthenticationProvider.d.ts.map +1 -0
- package/dist/Authentication/NoOpAuthenticationProvider.js +31 -0
- package/dist/Authentication/NoOpAuthenticationProvider.js.map +1 -0
- package/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +14 -0
- package/dist/Cli.js.map +1 -1
- package/dist/CliSettings.d.ts.map +1 -1
- package/dist/CliSettings.js +2 -1
- package/dist/CliSettings.js.map +1 -1
- package/dist/Constants/CommandRegistry.d.ts.map +1 -1
- package/dist/Constants/CommandRegistry.js +22 -4
- package/dist/Constants/CommandRegistry.js.map +1 -1
- package/dist/Constants/HelpStrings.d.ts +21 -3
- package/dist/Constants/HelpStrings.d.ts.map +1 -1
- package/dist/Constants/HelpStrings.js +53 -18
- package/dist/Constants/HelpStrings.js.map +1 -1
- package/dist/Constants.d.ts +2 -0
- package/dist/Constants.d.ts.map +1 -1
- package/dist/Constants.js +8 -0
- package/dist/Constants.js.map +1 -1
- package/dist/HttpClient/CliHttpClient.d.ts +1 -1
- package/dist/HttpClient/CliHttpClient.d.ts.map +1 -1
- package/dist/HttpClient/CliHttpClient.js +49 -2
- package/dist/HttpClient/CliHttpClient.js.map +1 -1
- package/dist/Logger/CliLogger.d.ts +1 -1
- package/dist/Logger/CliLogger.d.ts.map +1 -1
- package/dist/Logger/CliLogger.js +23 -4
- package/dist/Logger/CliLogger.js.map +1 -1
- package/dist/Logger/ConsoleWriter.d.ts +1 -0
- package/dist/Logger/ConsoleWriter.d.ts.map +1 -1
- package/dist/Logger/ConsoleWriter.js +3 -0
- package/dist/Logger/ConsoleWriter.js.map +1 -1
- package/dist/Logger/OneDSWriter.d.ts +1 -1
- package/dist/Logger/OneDSWriter.d.ts.map +1 -1
- package/dist/Logger/OneDSWriter.js +8 -3
- package/dist/Logger/OneDSWriter.js.map +1 -1
- package/dist/Testing/MockFetch.d.ts +23 -0
- package/dist/Testing/MockFetch.d.ts.map +1 -0
- package/dist/Testing/MockFetch.js +177 -0
- package/dist/Testing/MockFetch.js.map +1 -0
- package/dist/Testing/MockRoutes.d.ts +34 -0
- package/dist/Testing/MockRoutes.d.ts.map +1 -0
- package/dist/Testing/MockRoutes.js +226 -0
- package/dist/Testing/MockRoutes.js.map +1 -0
- package/dist/Testing/fixtures/connectors/commondataserviceforapps.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/commondataserviceforapps.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/commondataserviceforapps.js +25 -0
- package/dist/Testing/fixtures/connectors/commondataserviceforapps.js.map +1 -0
- package/dist/Testing/fixtures/connectors/dataverseContactEntity.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/dataverseContactEntity.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/dataverseContactEntity.js +55 -0
- package/dist/Testing/fixtures/connectors/dataverseContactEntity.js.map +1 -0
- package/dist/Testing/fixtures/connectors/excelonlinebusiness.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/excelonlinebusiness.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/excelonlinebusiness.js +1970 -0
- package/dist/Testing/fixtures/connectors/excelonlinebusiness.js.map +1 -0
- package/dist/Testing/fixtures/connectors/kusto.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/kusto.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/kusto.js +1333 -0
- package/dist/Testing/fixtures/connectors/kusto.js.map +1 -0
- package/dist/Testing/fixtures/connectors/msnweather.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/msnweather.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/msnweather.js +917 -0
- package/dist/Testing/fixtures/connectors/msnweather.js.map +1 -0
- package/dist/Testing/fixtures/connectors/office365groups.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/office365groups.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/office365groups.js +2144 -0
- package/dist/Testing/fixtures/connectors/office365groups.js.map +1 -0
- package/dist/Testing/fixtures/connectors/office365users.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/office365users.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/office365users.js +1969 -0
- package/dist/Testing/fixtures/connectors/office365users.js.map +1 -0
- package/dist/Testing/fixtures/connectors/onedriveforbusiness.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/onedriveforbusiness.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/onedriveforbusiness.js +4873 -0
- package/dist/Testing/fixtures/connectors/onedriveforbusiness.js.map +1 -0
- package/dist/Testing/fixtures/connectors/powerappsforappmakers.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/powerappsforappmakers.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/powerappsforappmakers.js +4682 -0
- package/dist/Testing/fixtures/connectors/powerappsforappmakers.js.map +1 -0
- package/dist/Testing/fixtures/connectors/sharepointTestListMetadata.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/sharepointTestListMetadata.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/sharepointTestListMetadata.js +1078 -0
- package/dist/Testing/fixtures/connectors/sharepointTestListMetadata.js.map +1 -0
- package/dist/Testing/fixtures/connectors/sharepointonline.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/sharepointonline.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/sharepointonline.js +28 -0
- package/dist/Testing/fixtures/connectors/sharepointonline.js.map +1 -0
- package/dist/Testing/fixtures/connectors/teams.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/teams.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/teams.js +9987 -0
- package/dist/Testing/fixtures/connectors/teams.js.map +1 -0
- package/dist/Testing/fixtures/connectors/translatorv2.d.ts +6 -0
- package/dist/Testing/fixtures/connectors/translatorv2.d.ts.map +1 -0
- package/dist/Testing/fixtures/connectors/translatorv2.js +307 -0
- package/dist/Testing/fixtures/connectors/translatorv2.js.map +1 -0
- package/dist/Testing/fixtures/index.d.ts +23 -0
- package/dist/Testing/fixtures/index.d.ts.map +1 -0
- package/dist/Testing/fixtures/index.js +96 -0
- package/dist/Testing/fixtures/index.js.map +1 -0
- package/dist/Types/LoggerTypes.types.d.ts +1 -0
- package/dist/Types/LoggerTypes.types.d.ts.map +1 -1
- package/dist/Utils/CoerceCliVersion.d.ts +27 -0
- package/dist/Utils/CoerceCliVersion.d.ts.map +1 -0
- package/dist/Utils/CoerceCliVersion.js +67 -0
- package/dist/Utils/CoerceCliVersion.js.map +1 -0
- package/dist/Utils/EnvVars.d.ts +1 -1
- package/dist/Utils/EnvVars.d.ts.map +1 -1
- package/dist/Utils/EnvVars.js.map +1 -1
- package/dist/Utils/EnvironmentRoutingUtils.d.ts +1 -2
- package/dist/Utils/EnvironmentRoutingUtils.d.ts.map +1 -1
- package/dist/Utils/EnvironmentRoutingUtils.js.map +1 -1
- package/dist/Utils/MaafAppConfig.d.ts +2 -0
- package/dist/Utils/MaafAppConfig.d.ts.map +1 -1
- package/dist/Utils/MaafAppConfig.js +6 -0
- package/dist/Utils/MaafAppConfig.js.map +1 -1
- package/dist/Utils/PinCli.d.ts +18 -13
- package/dist/Utils/PinCli.d.ts.map +1 -1
- package/dist/Utils/PinCli.js +37 -18
- package/dist/Utils/PinCli.js.map +1 -1
- package/dist/Utils/RepoOption.d.ts +1 -0
- package/dist/Utils/RepoOption.d.ts.map +1 -1
- package/dist/Utils/RepoOption.js +14 -0
- package/dist/Utils/RepoOption.js.map +1 -1
- package/dist/Utils/ShareAccessRouting.d.ts +78 -0
- package/dist/Utils/ShareAccessRouting.d.ts.map +1 -0
- package/dist/Utils/ShareAccessRouting.js +93 -0
- package/dist/Utils/ShareAccessRouting.js.map +1 -0
- package/dist/Verbs/AddConnector.d.ts +6 -6
- package/dist/Verbs/AddConnector.d.ts.map +1 -1
- package/dist/Verbs/AddConnector.js +62 -36
- package/dist/Verbs/AddConnector.js.map +1 -1
- package/dist/Verbs/AddConnectorFlows.d.ts +14 -2
- package/dist/Verbs/AddConnectorFlows.d.ts.map +1 -1
- package/dist/Verbs/AddConnectorFlows.js +67 -18
- package/dist/Verbs/AddConnectorFlows.js.map +1 -1
- package/dist/Verbs/ConnectorHelpers.d.ts +79 -6
- package/dist/Verbs/ConnectorHelpers.d.ts.map +1 -1
- package/dist/Verbs/ConnectorHelpers.js +280 -32
- package/dist/Verbs/ConnectorHelpers.js.map +1 -1
- package/dist/Verbs/CreateMaafApp.d.ts +8 -16
- package/dist/Verbs/CreateMaafApp.d.ts.map +1 -1
- package/dist/Verbs/CreateMaafApp.js +224 -96
- package/dist/Verbs/CreateMaafApp.js.map +1 -1
- package/dist/Verbs/DeleteMaafApp.d.ts +13 -9
- package/dist/Verbs/DeleteMaafApp.d.ts.map +1 -1
- package/dist/Verbs/DeleteMaafApp.js +41 -21
- package/dist/Verbs/DeleteMaafApp.js.map +1 -1
- package/dist/Verbs/Dev.d.ts.map +1 -1
- package/dist/Verbs/Dev.js +34 -3
- package/dist/Verbs/Dev.js.map +1 -1
- package/dist/Verbs/GetMaafApp.d.ts.map +1 -1
- package/dist/Verbs/GetMaafApp.js +18 -6
- package/dist/Verbs/GetMaafApp.js.map +1 -1
- package/dist/Verbs/Init.d.ts.map +1 -1
- package/dist/Verbs/Init.js +58 -5
- package/dist/Verbs/Init.js.map +1 -1
- package/dist/Verbs/ListConnectors.d.ts.map +1 -1
- package/dist/Verbs/ListConnectors.js +12 -1
- package/dist/Verbs/ListConnectors.js.map +1 -1
- package/dist/Verbs/ListMaafApps.d.ts +9 -8
- package/dist/Verbs/ListMaafApps.d.ts.map +1 -1
- package/dist/Verbs/ListMaafApps.js +30 -23
- package/dist/Verbs/ListMaafApps.js.map +1 -1
- package/dist/Verbs/MaafAppPermissions.d.ts +8 -17
- package/dist/Verbs/MaafAppPermissions.d.ts.map +1 -1
- package/dist/Verbs/MaafAppPermissions.js +84 -91
- package/dist/Verbs/MaafAppPermissions.js.map +1 -1
- package/dist/Verbs/Pack.d.ts +30 -1
- package/dist/Verbs/Pack.d.ts.map +1 -1
- package/dist/Verbs/Pack.js +179 -2
- package/dist/Verbs/Pack.js.map +1 -1
- package/dist/Verbs/RefreshDataSource.d.ts +15 -0
- package/dist/Verbs/RefreshDataSource.d.ts.map +1 -1
- package/dist/Verbs/RefreshDataSource.js +41 -27
- package/dist/Verbs/RefreshDataSource.js.map +1 -1
- package/dist/Verbs/RemoveDataSource.d.ts.map +1 -1
- package/dist/Verbs/RemoveDataSource.js +17 -3
- package/dist/Verbs/RemoveDataSource.js.map +1 -1
- package/dist/Verbs/ShareList.d.ts +10 -0
- package/dist/Verbs/ShareList.d.ts.map +1 -0
- package/dist/Verbs/ShareList.js +254 -0
- package/dist/Verbs/ShareList.js.map +1 -0
- package/dist/Verbs/VerbConstants.d.ts +56 -3
- package/dist/Verbs/VerbConstants.d.ts.map +1 -1
- package/dist/Verbs/VerbConstants.js +40 -3
- package/dist/Verbs/VerbConstants.js.map +1 -1
- package/dist/Verbs/index.d.ts +1 -0
- package/dist/Verbs/index.d.ts.map +1 -1
- package/dist/Verbs/index.js +1 -0
- package/dist/Verbs/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { createAsync, getPlayerServiceConfig, updateEnvironmentName, writeRepoConfig, } from '@microsoft/managed-apps-actions';
|
|
5
5
|
import { AppConfigFileName } from '@microsoft/managed-apps-common/config';
|
|
6
|
-
import { regionToCloudSafe } from '@microsoft/managed-apps-common/services';
|
|
6
|
+
import { HttpClientError, regionToCloudSafe } from '@microsoft/managed-apps-common/services';
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as os from 'os';
|
|
9
9
|
import * as path from 'path';
|
|
@@ -11,7 +11,7 @@ import { getCliLogger, getSettings } from '../CliSettings.js';
|
|
|
11
11
|
import { createDefaultStringOption, resolveEnvironmentId } from '../CliUtils.js';
|
|
12
12
|
import { CLI_BINARY_NAME } from '../Constants/CliConstants.js';
|
|
13
13
|
import { HelpStrings } from '../Constants/HelpStrings.js';
|
|
14
|
-
import { AppError, ConfigurationError, UsageError } from '../Errors/CliError.js';
|
|
14
|
+
import { AppError, AuthenticationError, ConfigurationError, NetworkError, UsageError, } from '../Errors/CliError.js';
|
|
15
15
|
import { printInfo, printJsonLine, printJsonSuccess, printSuccess, printWarning, } from '../Utils/ConsoleOutput.js';
|
|
16
16
|
import { withGitHubDcfRecoveryAsync } from '../Utils/DcfRecovery.js';
|
|
17
17
|
import { downloadTemplate, parseGithubTemplate } from '../Utils/DownloadTemplate.js';
|
|
@@ -25,6 +25,9 @@ import { parseRepoOption } from '../Utils/RepoOption.js';
|
|
|
25
25
|
import { CliSpinner } from '../Utils/Spinner.js';
|
|
26
26
|
import { CreateMaafAppArguments } from './VerbConstants.js';
|
|
27
27
|
const DEFAULT_TEMPLATE = 'github:microsoft/Managed-Apps/templates/vite8';
|
|
28
|
+
/** Commit message for the initial scaffold, shared by the auto-push and every
|
|
29
|
+
* manual-recovery hint */
|
|
30
|
+
const SCAFFOLD_COMMIT_MESSAGE = "Initial scaffold from 'ms app create'";
|
|
28
31
|
/**
|
|
29
32
|
* Creates a new MAAF app and (where applicable) sets up local source control.
|
|
30
33
|
*
|
|
@@ -93,7 +96,7 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
93
96
|
// ─── Preflight ────────────────────────────────────────────────────────────
|
|
94
97
|
// Run all local-only checks before any server call. Once createAsync
|
|
95
98
|
// succeeds the user has a MAAF app + GRS repo on the server; a later
|
|
96
|
-
// local-setup failure leaves them
|
|
99
|
+
// local-setup failure leaves them with created resources that need recovery.
|
|
97
100
|
// Anything we can validate without hitting the server belongs here.
|
|
98
101
|
//
|
|
99
102
|
// --repo path: requires git installed, writable target, and an empty target
|
|
@@ -139,7 +142,7 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
139
142
|
ensureRepoTargetDirectoryEmpty(appDir);
|
|
140
143
|
// Pre-network: cheap partial clone into a temp dir + ls-tree HEAD to detect
|
|
141
144
|
// a non-empty remote before createAsync runs. Catches the rejection case
|
|
142
|
-
// without leaving
|
|
145
|
+
// without leaving a server-side app or scaffolded files in
|
|
143
146
|
// appDir. Cheapest probe shape: --depth=1 --filter=blob:none --no-checkout
|
|
144
147
|
// pulls refs + the tip commit + its tree(s) only (no blob bytes, no working
|
|
145
148
|
// tree). On any probe error (network, auth, server doesn't support partial
|
|
@@ -188,8 +191,8 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
188
191
|
// now is async and may take seconds — long enough for the user (or another
|
|
189
192
|
// tool) to drop a file (or ms.config.json) into appDir. Repeating the check
|
|
190
193
|
// here keeps the TOCTOU window between "directory looks fine" and
|
|
191
|
-
// "createAsync succeeded" as small as possible without
|
|
192
|
-
//
|
|
194
|
+
// "createAsync succeeded" as small as possible without creating a server-side
|
|
195
|
+
// app before local invariants are verified.
|
|
193
196
|
if (repoType === 'github') {
|
|
194
197
|
ensureRepoTargetDirectoryEmpty(appDir);
|
|
195
198
|
}
|
|
@@ -240,21 +243,36 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
240
243
|
// DCF and retrying once; any other recognized server error is mapped to a
|
|
241
244
|
// friendly CLI error. Non-github flows pass through untouched. See
|
|
242
245
|
// `withGitHubDcfRecoveryAsync` for the full contract.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
//
|
|
247
|
+
// Any server error that escapes the DCF wrapper (all repo types) is routed
|
|
248
|
+
// through `translateCreateError`, which keys off the HTTP status code to
|
|
249
|
+
// surface an actionable message (e.g. a 403 becomes a permission error naming
|
|
250
|
+
// the environment) instead of the raw `HTTP error status: ...` blob from the RP.
|
|
251
|
+
let createResult;
|
|
252
|
+
try {
|
|
253
|
+
createResult = await withGitHubDcfRecoveryAsync(runCreate, {
|
|
254
|
+
repoType,
|
|
255
|
+
externalRepoUrl: remoteUrl,
|
|
256
|
+
tenantId,
|
|
257
|
+
jsonMode,
|
|
258
|
+
openBrowser,
|
|
259
|
+
verb: 'app create',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch (createError) {
|
|
263
|
+
const translated = translateCreateError(createError, { repoType, environmentId });
|
|
264
|
+
if (translated)
|
|
265
|
+
throw translated;
|
|
266
|
+
throw createError;
|
|
267
|
+
}
|
|
251
268
|
const { app, cloneUrl } = createResult;
|
|
252
269
|
// Only the `native` flow expects a GRS clone URL — it's the origin for the first
|
|
253
|
-
// local push to the auto-provisioned platform-managed repo
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
// `
|
|
270
|
+
// local push to the auto-provisioned platform-managed repo, which GRS seeds with
|
|
271
|
+
// an initial commit on `main`. `github` binds an external GitHub repo (no GRS)
|
|
272
|
+
// and `none` is by-design source-control-free, so neither produces a `cloneUrl`.
|
|
273
|
+
// Missing on `native` is a server bug — the hoisted check below guards both the
|
|
274
|
+
// pretty-output `(clone URL: undefined)` and JSON-envelope `gitRemoteUrl: undefined`
|
|
275
|
+
// failure modes that would otherwise leak through.
|
|
258
276
|
if (repoType === 'native' && !cloneUrl) {
|
|
259
277
|
const recoveryHint = buildOrphanedAppRecoveryHint(app.id, app.environmentId);
|
|
260
278
|
throw new AppError(`App '${app.displayName}' was created on the server, but creating a git repository failed. ` +
|
|
@@ -263,10 +281,11 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
263
281
|
recoveryHint);
|
|
264
282
|
}
|
|
265
283
|
// Set up local files. Runs after the API calls succeed; if any step here
|
|
266
|
-
// fails the server app is already created
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
284
|
+
// fails the server app is already created. The branch per `repoType`:
|
|
285
|
+
// - native: init local git repo + GCM config for the auto-provisioned GRS
|
|
286
|
+
// repo (seeded with an initial commit on `main`), overlay the
|
|
287
|
+
// template, then write ms.config.json. The push phase reconciles
|
|
288
|
+
// the scaffold onto that seeded history before pushing.
|
|
270
289
|
// - github: clone the user's external repo, sanity-check it's empty,
|
|
271
290
|
// overlay the template, then write ms.config.json.
|
|
272
291
|
// - none: no git, no clone — mkdir -p, overlay the template, write ms.config.json.
|
|
@@ -302,7 +321,6 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
302
321
|
gitUrl: cloneUrl,
|
|
303
322
|
loginAuthority: argumentProvider.getLoginAuthority(tenantId),
|
|
304
323
|
authResource: argumentProvider.getAuthResourceUrl(),
|
|
305
|
-
jsonMode,
|
|
306
324
|
});
|
|
307
325
|
}
|
|
308
326
|
else {
|
|
@@ -315,12 +333,13 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
315
333
|
printInfo(`Fetching template '${template}' into ${appDir}...`);
|
|
316
334
|
}
|
|
317
335
|
downloadTemplate(template, { dir: appDir });
|
|
318
|
-
// Pin the
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
// Runs for every repo flow (the template, and
|
|
322
|
-
// lands via downloadTemplate above regardless of
|
|
323
|
-
|
|
336
|
+
// Pin the CLI into the app so a later package install provides a project-local
|
|
337
|
+
// `ms` — resolved by `npx ms` / `pnpm exec` / `bun x` ahead of any globally-
|
|
338
|
+
// installed CLI — at the version it was created with (or the latest published
|
|
339
|
+
// version for a prerelease build). Runs for every repo flow (the template, and
|
|
340
|
+
// therefore its package.json, lands via downloadTemplate above regardless of
|
|
341
|
+
// `--repo`).
|
|
342
|
+
await pinCliIntoAppPackageJson(appDir, jsonMode, argumentProvider.getHttpClient());
|
|
324
343
|
const config = buildMaafAppConfig({
|
|
325
344
|
appId: app.id,
|
|
326
345
|
appDisplayName: app.displayName,
|
|
@@ -346,31 +365,24 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
346
365
|
// has no remote, so it's skipped. Push failure is non-fatal — see the warning
|
|
347
366
|
// path in commitAndPushScaffoldAsync.
|
|
348
367
|
if (repoType !== 'none') {
|
|
349
|
-
|
|
350
|
-
// OAuth credentials, so we can label an auth/network failure correctly. The
|
|
351
|
-
// github flow uses the developer's own credentials (gh / SSH / PAT) we don't
|
|
352
|
-
// control, so it's left unclassified. Unlike fetch, push is non-fatal: the
|
|
353
|
-
// classified error is caught and turned into a PushOutcome, so its exit code
|
|
354
|
-
// never propagates. Classification only relabels the cause-specific message —
|
|
355
|
-
// surfaced as the human-mode warning and carried in the --json error payload.
|
|
356
|
-
const pushClassify = repoType === 'native' && cloneUrl
|
|
357
|
-
? { action: `pushing to ${gitHostLabel(cloneUrl)}` }
|
|
358
|
-
: undefined;
|
|
359
|
-
pushOutcome = await commitAndPushScaffoldAsync(appDir, jsonMode, pushClassify);
|
|
368
|
+
pushOutcome = await commitAndPushScaffoldAsync(appDir, jsonMode, repoType === 'native' ? cloneUrl : undefined);
|
|
360
369
|
}
|
|
361
370
|
}
|
|
362
371
|
catch (setupError) {
|
|
363
372
|
// UsageError carries already-actionable messages (e.g. "repo already contains an
|
|
364
|
-
// app — try ms app init")
|
|
365
|
-
//
|
|
366
|
-
|
|
373
|
+
// app — try ms app init") and, for the non-native flows that raise it, its own
|
|
374
|
+
// recovery hint. Let those through unwrapped. The native flow has already created
|
|
375
|
+
// the server app + repo by the time any setup step throws, so it always routes
|
|
376
|
+
// through the generic re-messaging below to surface the cleanup hint.
|
|
377
|
+
if (setupError instanceof UsageError && repoType !== 'native') {
|
|
367
378
|
throw setupError;
|
|
368
379
|
}
|
|
369
380
|
const detail = setupError instanceof Error ? setupError.message : String(setupError);
|
|
370
381
|
const message = `App '${app.displayName}' was created, but local setup failed: ${detail}\n` +
|
|
382
|
+
`The local directory ${appDir} may contain partial files; remove them or choose a new directory before retrying.\n` +
|
|
371
383
|
buildOrphanedAppRecoveryHint(app.id, app.environmentId);
|
|
372
384
|
// Re-message a typed AppError in place rather than re-wrapping it, so the
|
|
373
|
-
// failure category survives the
|
|
385
|
+
// failure category survives the setup-recovery prefix intact: a classified
|
|
374
386
|
// AuthenticationError keeps its exit code (3), name, errorData, AND its
|
|
375
387
|
// subclass identity (`instanceof AuthenticationError` still holds for any
|
|
376
388
|
// future handler that branches on the class). Constructing a fresh AppError
|
|
@@ -422,31 +434,41 @@ export async function createMaafAppAsync(argumentProvider) {
|
|
|
422
434
|
* progress prose stays out of our pretty output). Stderr handling follows the
|
|
423
435
|
* runGit policy: the un-classified `add`/`commit` steps re-emit git's stderr on
|
|
424
436
|
* failure, and the `push` step either streams stderr live (un-classified github
|
|
425
|
-
* flow, `network: true`) or pipes it for classification (native flow
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
* only shapes the warning text and the `--json` error payload.
|
|
437
|
+
* flow, `network: true`) or pipes it for classification (native flow) — either
|
|
438
|
+
* way git's own error context reaches the user alongside our warning. A
|
|
439
|
+
* classified push error is caught here, so its exit code never propagates;
|
|
440
|
+
* classification only shapes the warning text and the `--json` error payload.
|
|
430
441
|
*
|
|
431
442
|
* Commit identity (user.name / user.email) is read by git itself from
|
|
432
443
|
* local-then-global config — we pass nothing. Missing identity surfaces as
|
|
433
444
|
* git's own descriptive commit error via runGit's stderr handling.
|
|
434
445
|
*/
|
|
435
|
-
export async function commitAndPushScaffoldAsync(appDir, jsonMode,
|
|
446
|
+
export async function commitAndPushScaffoldAsync(appDir, jsonMode, nativeGitUrl) {
|
|
436
447
|
// Full add+commit+push sequence: any of the three commands may have been the
|
|
437
448
|
// one that failed (e.g. `git commit` errors when user.name/user.email aren't
|
|
438
449
|
// set), so re-running just `git push` wouldn't recover. The sequence is
|
|
439
450
|
// idempotent: rerunning `git add .` and `git commit` on an already-committed
|
|
440
451
|
// state either no-ops or errors with "nothing to commit", in which case
|
|
441
452
|
// `git push` still pushes the existing commit.
|
|
442
|
-
|
|
453
|
+
//
|
|
454
|
+
// Native (GRS) has no safe one-line recovery: the failure could be at the
|
|
455
|
+
// fetch/reconcile step (scaffold not yet re-parented onto the seeded main) or
|
|
456
|
+
// at the final push (already re-parented), and a single copy-paste command
|
|
457
|
+
// can't be correct for both. So we surface no command for native and instead
|
|
458
|
+
// point the user at the local scaffold below.
|
|
459
|
+
const retryCommand = nativeGitUrl
|
|
460
|
+
? undefined
|
|
461
|
+
: `git add . && git commit -m "${SCAFFOLD_COMMIT_MESSAGE}" && git push -u origin HEAD`;
|
|
443
462
|
try {
|
|
444
463
|
runGit(['add', '.'], { cwd: appDir });
|
|
445
|
-
runGit(['commit', '-m',
|
|
464
|
+
runGit(['commit', '-m', SCAFFOLD_COMMIT_MESSAGE], { cwd: appDir });
|
|
465
|
+
if (nativeGitUrl) {
|
|
466
|
+
reconcileNativeScaffoldWithSeededMain(appDir, nativeGitUrl, jsonMode);
|
|
467
|
+
}
|
|
446
468
|
runGit(['push', '-u', 'origin', 'HEAD'], {
|
|
447
469
|
cwd: appDir,
|
|
448
470
|
network: true,
|
|
449
|
-
classify:
|
|
471
|
+
classify: nativeGitUrl ? { action: `pushing to ${gitHostLabel(nativeGitUrl)}` } : undefined,
|
|
450
472
|
});
|
|
451
473
|
return { success: true };
|
|
452
474
|
}
|
|
@@ -464,18 +486,68 @@ export async function commitAndPushScaffoldAsync(appDir, jsonMode, pushClassify)
|
|
|
464
486
|
// carries the actionable, cause-specific guidance — plus git's own output —
|
|
465
487
|
// in its message, but runGit's classify path throws *before* re-emitting
|
|
466
488
|
// stderr, so without this the human-readable terminal would only see the
|
|
467
|
-
// generic
|
|
489
|
+
// generic hint. (Unclassified add/commit failures and the live-stderr
|
|
468
490
|
// github push already surfaced git's output, so only re-print for the
|
|
469
491
|
// classified case to avoid duplicate noise.)
|
|
470
492
|
if (err instanceof AppError) {
|
|
471
493
|
printWarning(err.message);
|
|
472
494
|
}
|
|
495
|
+
const finishHint = retryCommand
|
|
496
|
+
? `To finish wiring it up, run:\n ${retryCommand}`
|
|
497
|
+
: `Resolve the git issue above, then finish the initial push from ${appDir}.`;
|
|
473
498
|
printWarning(`Could not commit and push the initial scaffold. Your app and local scaffold are ready. ` +
|
|
474
|
-
|
|
499
|
+
finishHint);
|
|
475
500
|
}
|
|
476
501
|
return { success: false, error: errorPayload, retryCommand };
|
|
477
502
|
}
|
|
478
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Reconciles the freshly committed local scaffold with the platform-seeded
|
|
506
|
+
* `main` branch just before the native (GRS) push. GRS provisions the repo
|
|
507
|
+
* with an initial commit on `main` (a README).
|
|
508
|
+
*
|
|
509
|
+
* 1. `git fetch origin` — bring the seed commit into origin/main
|
|
510
|
+
* (retried once on a transient auth miss).
|
|
511
|
+
* 2. `git reset --soft origin/main` — move HEAD onto the seed while keeping the
|
|
512
|
+
* scaffold tree fully staged.
|
|
513
|
+
* 3. `git commit` — record the scaffold as a child of the seed.
|
|
514
|
+
*/
|
|
515
|
+
function reconcileNativeScaffoldWithSeededMain(appDir, gitUrl, jsonMode) {
|
|
516
|
+
announceGitAuthentication(gitUrl, jsonMode, 'the initial git fetch and push');
|
|
517
|
+
fetchOriginWithRetry(appDir, gitUrl);
|
|
518
|
+
const seededMain = runGit(['rev-parse', '--verify', 'origin/main'], {
|
|
519
|
+
cwd: appDir,
|
|
520
|
+
allowFailure: true,
|
|
521
|
+
emitStderr: false,
|
|
522
|
+
});
|
|
523
|
+
if (!seededMain.success) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
runGit(['reset', '--soft', 'origin/main'], { cwd: appDir });
|
|
527
|
+
runGit(['commit', '-m', SCAFFOLD_COMMIT_MESSAGE], { cwd: appDir });
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Fetches `origin`, retrying exactly once if the first attempt fails.
|
|
531
|
+
*
|
|
532
|
+
* The first attempt is silent (`allowFailure`, stderr piped-not-emitted) so a
|
|
533
|
+
* recoverable miss produces no scary output.
|
|
534
|
+
*/
|
|
535
|
+
function fetchOriginWithRetry(appDir, gitUrl) {
|
|
536
|
+
const firstAttempt = runGit(['fetch', 'origin'], {
|
|
537
|
+
cwd: appDir,
|
|
538
|
+
network: true,
|
|
539
|
+
allowFailure: true,
|
|
540
|
+
emitStderr: false,
|
|
541
|
+
});
|
|
542
|
+
if (firstAttempt.success) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
runGit(['fetch', 'origin'], {
|
|
546
|
+
cwd: appDir,
|
|
547
|
+
network: true,
|
|
548
|
+
classify: { action: `fetching from ${gitHostLabel(gitUrl)}` },
|
|
549
|
+
});
|
|
550
|
+
}
|
|
479
551
|
const GIT_CLONE_ACCESS_FAILURE_SIGNATURES = [
|
|
480
552
|
'repository not found',
|
|
481
553
|
'fatal: repository',
|
|
@@ -527,7 +599,7 @@ function buildGitHubCloneAccessErrorMessage({ directory, repoUrl }) {
|
|
|
527
599
|
return `Failed to clone '${repoUrl}' into '${directory}'. ` + gitHubAccessFailureGuidance();
|
|
528
600
|
}
|
|
529
601
|
// Pre-create variant: the probe runs BEFORE the server app is created, so the
|
|
530
|
-
// lead makes explicit that nothing was created (no
|
|
602
|
+
// lead makes explicit that nothing was created (no cleanup / no recovery hint).
|
|
531
603
|
function buildGitHubAccessPreflightErrorMessage(repoUrl) {
|
|
532
604
|
return (`Cannot access repository '${repoUrl}', so no app was created. ` + gitHubAccessFailureGuidance());
|
|
533
605
|
}
|
|
@@ -574,14 +646,11 @@ export function cloneRepo({ directory, repoUrl }) {
|
|
|
574
646
|
});
|
|
575
647
|
}
|
|
576
648
|
}
|
|
577
|
-
export function initLocalRepo({ directory, gitUrl, loginAuthority, authResource,
|
|
649
|
+
export function initLocalRepo({ directory, gitUrl, loginAuthority, authResource, }) {
|
|
578
650
|
const gcmConfig = buildGcmConfigOrThrow({ gitUrl, loginAuthority, authResource });
|
|
579
651
|
gitInit(directory);
|
|
580
652
|
writeGcmConfig(directory, gcmConfig);
|
|
581
653
|
addOrUpdateRemote(directory, gitUrl);
|
|
582
|
-
announceGitAuthentication(gitUrl, jsonMode);
|
|
583
|
-
fetchOrigin(directory, gitUrl);
|
|
584
|
-
checkoutMainIfPresent(directory);
|
|
585
654
|
}
|
|
586
655
|
/**
|
|
587
656
|
* Builds the per-host GCM OAuth config, re-wrapping any URL/protocol validation
|
|
@@ -596,12 +665,19 @@ function buildGcmConfigOrThrow({ gitUrl, loginAuthority, authResource, }) {
|
|
|
596
665
|
throw new ConfigurationError(`Git credential helper setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
597
666
|
}
|
|
598
667
|
}
|
|
599
|
-
/**
|
|
668
|
+
/**
|
|
669
|
+
* Creates the target directory (idempotent) and initializes an empty git repo
|
|
670
|
+
* whose initial branch is `main`.
|
|
671
|
+
*/
|
|
600
672
|
function gitInit(directory) {
|
|
601
673
|
fs.mkdirSync(directory, { recursive: true });
|
|
602
674
|
runGit(['init'], { cwd: directory });
|
|
675
|
+
// use `git symbolic-ref HEAD refs/heads/main` to set the initial branch to `main` instead of `master`. This is
|
|
676
|
+
// used instead of `git init -b main` / `-c init.defaultBranch=main` because both
|
|
677
|
+
// of those landed in git 2.28, one minor above this package's enforced 2.27 floor
|
|
678
|
+
runGit(['symbolic-ref', 'HEAD', 'refs/heads/main'], { cwd: directory });
|
|
603
679
|
}
|
|
604
|
-
/** Writes GCM config before
|
|
680
|
+
/** Writes GCM config before pushing so the first network call is already authenticated. */
|
|
605
681
|
function writeGcmConfig(directory, gcmConfig) {
|
|
606
682
|
for (const [key, value] of gcmConfig) {
|
|
607
683
|
runGit(['config', '--local', key, value], { cwd: directory });
|
|
@@ -633,7 +709,7 @@ function addOrUpdateRemote(directory, gitUrl) {
|
|
|
633
709
|
/**
|
|
634
710
|
* Best-effort host label for a git remote URL. Returns the URL's host, or — when
|
|
635
711
|
* the string isn't a parseable URL — the original string. Used for human-facing
|
|
636
|
-
* action labels (auth/
|
|
712
|
+
* action labels (auth/push messages) where a malformed URL must not turn a
|
|
637
713
|
* cosmetic label into a thrown `Invalid URL` that masquerades as the real
|
|
638
714
|
* failure. The platform issues these URLs, so the fallback is effectively
|
|
639
715
|
* unreachable, but the guard keeps the label site from becoming a crash site.
|
|
@@ -647,43 +723,19 @@ function gitHostLabel(gitUrl) {
|
|
|
647
723
|
}
|
|
648
724
|
}
|
|
649
725
|
/**
|
|
650
|
-
* Prints the pre-
|
|
726
|
+
* Prints the pre-network authentication notice. The first git network call triggers
|
|
651
727
|
* GCM's OAuth flow if the user has no cached credentials, which can pop a
|
|
652
728
|
* browser without warning. Tell the user up front so they don't see the browser
|
|
653
729
|
* as a surprise (and know which host / account to authenticate against). Skipped
|
|
654
730
|
* under `--json` so the prose doesn't end up interleaved with the JSON envelope
|
|
655
731
|
* on stdout.
|
|
656
732
|
*/
|
|
657
|
-
function announceGitAuthentication(gitUrl, jsonMode) {
|
|
733
|
+
function announceGitAuthentication(gitUrl, jsonMode, action) {
|
|
658
734
|
if (!jsonMode) {
|
|
659
|
-
printInfo(`Authenticating to ${gitHostLabel(gitUrl)} for
|
|
735
|
+
printInfo(`Authenticating to ${gitHostLabel(gitUrl)} for ${action}. ` +
|
|
660
736
|
'A browser window may open. Sign in with the account associated with this tenant.');
|
|
661
737
|
}
|
|
662
738
|
}
|
|
663
|
-
/** Fetches `origin`, classifying any auth/network failure against the remote host. */
|
|
664
|
-
function fetchOrigin(directory, gitUrl) {
|
|
665
|
-
runGit(['fetch', 'origin'], {
|
|
666
|
-
cwd: directory,
|
|
667
|
-
network: true,
|
|
668
|
-
classify: { action: `fetching from ${gitHostLabel(gitUrl)}` },
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Checks out `main` tracking `origin/main` when that ref exists. Probe for
|
|
673
|
-
* origin/main locally (no network needed after fetch). A failed probe means the
|
|
674
|
-
* ref doesn't exist (empty GRS repo); allowFailure turns that expected case into
|
|
675
|
-
* a `success: false` signal instead of a throw.
|
|
676
|
-
*/
|
|
677
|
-
function checkoutMainIfPresent(directory) {
|
|
678
|
-
const mainProbe = runGit(['rev-parse', '--verify', 'origin/main'], {
|
|
679
|
-
cwd: directory,
|
|
680
|
-
allowFailure: true,
|
|
681
|
-
emitStderr: false,
|
|
682
|
-
});
|
|
683
|
-
if (mainProbe.success) {
|
|
684
|
-
runGit(['checkout', '-b', 'main', 'origin/main'], { cwd: directory });
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
739
|
// ─── Recovery hint for post-create failures ─────────────────────────────────
|
|
688
740
|
// Once createAsync returns, the MAAF app + GRS repo exist on the server; any
|
|
689
741
|
// later failure (no gitUrl, initLocalRepo, downloadTemplate, writeRepoConfig)
|
|
@@ -695,10 +747,86 @@ export function buildOrphanedAppRecoveryHint(serverAppId, environmentId) {
|
|
|
695
747
|
`If you want to clean it up before retrying, run the following command:\n` +
|
|
696
748
|
` ${CLI_BINARY_NAME} app delete --app ${serverAppId} --environment-id ${environmentId} --force`);
|
|
697
749
|
}
|
|
750
|
+
/** Extracts `error.message` from a JSON `{ error: { message } }` envelope; `undefined` otherwise. */
|
|
751
|
+
function extractServerMessage(err) {
|
|
752
|
+
if (!err.body)
|
|
753
|
+
return undefined;
|
|
754
|
+
try {
|
|
755
|
+
const parsed = JSON.parse(err.body);
|
|
756
|
+
if (typeof parsed?.error?.message === 'string' && parsed.error.message.trim()) {
|
|
757
|
+
return parsed.error.message;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
catch {
|
|
761
|
+
// not JSON; fall through
|
|
762
|
+
}
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Translates a server error thrown while creating the app (the `createAsync`
|
|
767
|
+
* RP/GRS calls) into a user-facing {@link AppError}, or returns `null` to signal
|
|
768
|
+
* "not handled — propagate the original". For the handled status codes below we
|
|
769
|
+
* key off the HTTP status to pick the error category (which drives the exit code)
|
|
770
|
+
* and surface an actionable message rather than the raw HTTP error blob
|
|
771
|
+
* (`HTTP error status: 403 for POST ...: {...}`). Unrecognized statuses (e.g. 500)
|
|
772
|
+
* return `null` and propagate unchanged, so the raw error can still surface for
|
|
773
|
+
* those — we only suppress the blob for the statuses we explicitly handle.
|
|
774
|
+
*
|
|
775
|
+
* The RP returns a structured `{ error: { code, message } }` envelope whose
|
|
776
|
+
* `message` is the source of truth — it names the offending principal and
|
|
777
|
+
* environment (e.g. "User '<oid>' does not have permission to create
|
|
778
|
+
* repositories in environment '<id>'."). We prefer that server message and fall
|
|
779
|
+
* back to CLI-generated text only when the body carries no usable message.
|
|
780
|
+
*
|
|
781
|
+
* Only translates {@link HttpClientError}. Errors already mapped to a typed
|
|
782
|
+
* {@link AppError} by the GitHub DCF recovery path (see `withGitHubDcfRecoveryAsync`)
|
|
783
|
+
* are passed through untouched.
|
|
784
|
+
*/
|
|
785
|
+
function translateCreateError(err, ctx) {
|
|
786
|
+
if (!(err instanceof HttpClientError))
|
|
787
|
+
return null;
|
|
788
|
+
const { environmentId } = ctx;
|
|
789
|
+
// Prefer the RP's own `error.message` — it's the source of truth and carries
|
|
790
|
+
// specifics (principal object id, environment id) the CLI can't reconstruct.
|
|
791
|
+
const serverMessage = extractServerMessage(err);
|
|
792
|
+
// The `native` (default) flow auto-provisions a platform-managed GRS repository,
|
|
793
|
+
// so a permission failure there is specifically about repository creation. The
|
|
794
|
+
// `github` / `none` flows don't provision a repo, so the denial is about creating
|
|
795
|
+
// the app itself. Used only for the fallback wording when the RP sends no message.
|
|
796
|
+
const creatingRepo = ctx.repoType === 'native';
|
|
797
|
+
// Operation-agnostic so it reads correctly whether it's appended to the RP's
|
|
798
|
+
// "...create repositories..." message, the "...create an app..." fallback, or
|
|
799
|
+
// the native/github variants — avoids a "repositories" vs "apps" mismatch.
|
|
800
|
+
const grantHint = 'Ask an admin to grant you the required permission in this environment.';
|
|
801
|
+
if (err.status === 401) {
|
|
802
|
+
return new AuthenticationError(serverMessage ??
|
|
803
|
+
'Your sign-in session has expired or is invalid. Run `ms auth login` and try again.');
|
|
804
|
+
}
|
|
805
|
+
if (err.status === 403) {
|
|
806
|
+
const base = serverMessage ??
|
|
807
|
+
(creatingRepo
|
|
808
|
+
? `You do not have permission to create repositories in environment ${environmentId}.`
|
|
809
|
+
: `You do not have permission to create an app in environment ${environmentId}.`);
|
|
810
|
+
return new AuthenticationError(`${base} ${grantHint}`);
|
|
811
|
+
}
|
|
812
|
+
if (err.status === 404) {
|
|
813
|
+
return new UsageError(serverMessage ??
|
|
814
|
+
`Environment ${environmentId} was not found. Verify the environment id and that you have access to it.`);
|
|
815
|
+
}
|
|
816
|
+
if (err.status === 409) {
|
|
817
|
+
return new UsageError(serverMessage ??
|
|
818
|
+
`An app with the same name already exists in environment ${environmentId}. Choose a different name.`);
|
|
819
|
+
}
|
|
820
|
+
if (err.status === 429 || err.status === 503) {
|
|
821
|
+
return new NetworkError(serverMessage ??
|
|
822
|
+
'The service is temporarily unavailable or rate-limited. Wait a moment and try again.');
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
698
826
|
// ─── Preflight helpers ──────────────────────────────────────────────────────
|
|
699
827
|
// All checks below run before any server call so a local misconfiguration
|
|
700
|
-
// (bad template source, unwritable directory, …) fails fast
|
|
701
|
-
//
|
|
828
|
+
// (bad template source, unwritable directory, …) fails fast before it creates
|
|
829
|
+
// a MAAF app + GRS repo on the server. The git presence + version
|
|
702
830
|
// check lives in `Utils/GitVersionChecker.ts`.
|
|
703
831
|
// ────────────────────────────────────────────────────────────────────────────
|
|
704
832
|
export function ensureTemplateSourceValid(source) {
|
|
@@ -747,13 +875,13 @@ export function ensureRepoTargetDirectoryEmpty(appDir) {
|
|
|
747
875
|
*
|
|
748
876
|
* Failure semantics:
|
|
749
877
|
* - Non-empty tree at HEAD → throws `UsageError` with the "is not empty" /
|
|
750
|
-
* "ms app init" actionable lead, BEFORE createAsync.
|
|
878
|
+
* "ms app init" actionable lead, BEFORE createAsync. Nothing created.
|
|
751
879
|
* - Empty tree (incl. the cleaned-up-repo edge case: commit1 add, commit2
|
|
752
880
|
* delete all) → ls-tree returns no lines → returns without throwing.
|
|
753
881
|
* - Clone fails with a recognizable access/auth signature (private repo
|
|
754
882
|
* hidden as "not found", missing local credentials, 403, …) → throws
|
|
755
883
|
* `ConfigurationError` BEFORE createAsync so we never create a server-side
|
|
756
|
-
* app the user can't clone
|
|
884
|
+
* app the user can't clone.
|
|
757
885
|
* - Any other git error (network blip, server doesn't support --filter,
|
|
758
886
|
* temp-fs hiccup, …) → swallow and return (fail OPEN). The subsequent
|
|
759
887
|
* cloneRepo surfaces the friendlier message for the user-facing failure;
|
|
@@ -775,7 +903,7 @@ export function ensureExternalRepoIsEmpty(repoUrl) {
|
|
|
775
903
|
// Clone failed. When git reports a recognizable access/auth failure
|
|
776
904
|
// (a private repo hidden as "not found", missing local credentials, a
|
|
777
905
|
// 403, etc.) fail FAST here — before createAsync — so we never create a
|
|
778
|
-
// server-side app the user can't clone
|
|
906
|
+
// server-side app the user can't clone. For ambiguous or
|
|
779
907
|
// transient errors (server doesn't support --filter, network blip, a
|
|
780
908
|
// temp-fs hiccup, …) stay fail-OPEN and let the later cloneRepo surface
|
|
781
909
|
// the friendlier, fuller message.
|