Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Bug CCD Management package fails to upload content if editor is running in batch mode

Discussion in 'Unity Cloud Content Delivery' started by n00levoy, Feb 12, 2023.

  1. n00levoy


    Nov 6, 2018
    We are using a C# script in the Unity editor to execute some steps of build pipeline, like building addessables, setting up scripting parameters or build options and building player itself. It is implemented as static void method as described here. I'm trying to add another step to that sequence, and it will upload freshly built addressable bundles to the CCD service.

    While this step works fine in editor started as GUI process, it throws an exception when run from CLI in batch mode, which is essential for CI builds:
    Code (CSharp):
    1. <b>[UploadToUnityCloudStep]</b>: Cloud Services must enabled and connected to a Unity Cloud Project.
    This exception pops up from this line:
    Code (CSharp):
    1. await CcdManagement.Instance.CreateOrUpdateEntryByPathAsync(entryByPathOptions, entryOptions);
    This method contains a following block of code:
    Code (CSharp):
    1. if (string.IsNullOrEmpty(CcdManagement.projectid))
    2. {
    3.     throw new CcdManagementException(CommonErrorCodes.InvalidRequest, SERVICES_ERROR_MSG);
    4. }
    So situation looks like that there is no cloud project ID set at the time of the execution of this block of code.

    I tried to run the following method in Unity, starting it from terminal:
    Code (CSharp):
    1. public static async void Test()
    2. {
    3.     while (string.IsNullOrEmpty(CloudProjectSettings.projectId))
    4.     {
    5.         Debug.Log("Awaiting Cloud Project Binding");
    6.         await UniTask.Yield();
    7.     }
    9.     Debug.Log($"Cloud Project Id: {CloudProjectSettings.projectId}");
    10.     EditorApplication.Exit(0);
    11. }
    If i execute it without the -batchmode parameter, it starts an editor in the GUI mode as usual, Unity post some logs from the while block to console and then logs a synced project id. If i execute the same command in batch mode, the script execution never leaves the while block and logs "Awaiting..." endlessly.

    While comparing editor logs of both cases I noticed that the GUI process has some lines, and the batch mode process doesn't:
    Code (CSharp):
    1. [collab-accelerator] discovery started due to a new cloud project binding
    2. UPID Received 'xxxxxxxxxxxxxxxxxxxxxxxxxx'.
    I guess something triggers Unity Services project sync in GUI, but it doesn't happen in batch mode. Everything connected to such sync which I managed to find leads to the UnityConnect class, and it is internal to UnityEditor.CoreModule.dll

    So what should I do to refresh the project connection to Unity Services in batch mode? Am I missing something here?

    Tried everything above with following configurations:
    • Unity 2021.3.5f1, 2021.3.18f1
    • Addressables 1.21.2
    • CCD Management 2.2.2
    • Services Core 1.7.1
    Last edited: Feb 12, 2023
  2. TruJordan


    Dec 13, 2022
    Has there been any update or workaround for this bug? I'm running into the same issue still. There is a very similar issue here as well.

    I tried adding the -username and -password options since I thought they might be required, but still got the same error unfortunately.

    Unity 2020.3.23f1
    Addressables 1.21.9
    CCD Management 2.2.2
  3. n00levoy


    Nov 6, 2018
    In the end I've given up on using CCD Management SDK for batchmode and end up invoking CCD CLI tool commands from C# code. It is actually even easier than using the SDK methods if you don't have any special cases where the CLI tool commands are unable to satisfy your needs.

    So you just start the ucd commands with a function that looks like this:
    Code (CSharp):
    1. private async Task StartProcess(params string[] arguments)
    2. {
    3.     var source = new TaskCompletionSource<string>();
    4.     var outputBuilder = new StringBuilder();
    5.     var errorBuilder = new StringBuilder();
    7.     var fullArgs = $"{string.Join(" ", arguments)}";
    9.     var process = new Process
    10.     {
    11.         StartInfo =
    12.         {
    13.             FileName = %path_to_ucd_tool%,
    14.             Arguments = fullArgs,
    15.             UseShellExecute = false,
    16.             CreateNoWindow = true,
    17.             RedirectStandardOutput = true,
    18.             RedirectStandardError = true
    19.         },
    20.         EnableRaisingEvents = true
    21.     };
    23.     process.Exited += (_, _) =>
    24.     {
    25.         var output = outputBuilder.ToString();
    27.         Debug.Log($"{fullArgs} output:\n{output}");
    29.         var error = errorBuilder.ToString();
    31.         if (!string.IsNullOrEmpty(error))
    32.         {
    33.             Debug.Log($"{fullArgs} error: {error}");
    34.             source.SetException(new Exception($"ucd error {process.ExitCode}: {error}"));
    35.         }
    36.         else
    37.         {
    38.             source.SetResult(output);
    39.         }
    41.         process.Dispose();
    42.     };
    44.     process.OutputDataReceived += (_, ea) =>
    45.     {
    46.         if (!string.IsNullOrEmpty(ea.Data))
    47.         {
    48.             outputBuilder.AppendLine(ea.Data);
    49.         }
    50.     };
    52.     process.ErrorDataReceived += (_, ea) =>
    53.     {
    54.         if (!string.IsNullOrEmpty(ea.Data))
    55.         {
    56.             errorBuilder.AppendLine(ea.Data);
    57.         }
    58.     };
    60.     process.Start();
    62.     process.BeginOutputReadLine();
    63.     process.BeginErrorReadLine();
    65.     return await source.Task;
    66. }
    You can use this function in the following way:
    Code (CSharp):
    1. await StartProcess("auth login", apiKey);
    3. await StartProcess(
    4.     "config set",
    5.     $"environment {environmentId}",
    6.     $"--project {projectId}");
    8. await StartProcess("config set", $"bucket {bucketId}");
    10. await StartProcess(
    11.     "entries sync",
    12.     buildPath,
    13.     "--delete",
    14.     "--release",
    15.     $"--release-notes '{appVersion} ({buildVersion})'",
    16.     $"--badge {badge}");
    SebT_Unity likes this.
  4. SebT_Unity


    Unity Technologies

    Jun 21, 2021
    Hi Folks,
    CCD Management is meant to be used within the editor only. I am currently speaking to the development team regarding this and looking to update documentation to make sure we clearly say this unless changed in future iterations. @n00levoy solution is the best way forward when working with build pipelines. Thanks for sharing your solution.
  5. TruJordan


    Dec 13, 2022
    Thank you both for the updates and the helpful code sample. I'll be moving forward with the CLI, then.
  6. JonAtHeyCat


    Feb 20, 2022
    I assume using CCD during the pre-export method execution in Unity Cloud Build is a no-no as well.
    as I'm getting a big ol'

    [warning] [2024-03-21T14:59:22Z - Unity] EXCEPTION: CcdManagementException: Cloud Services must enabled and connected to a Unity Cloud Project.

    in my UCB log
    Personally I'll probably fall back on either this CLI approach or using unity web requests and the rest admin api
  7. timtunity3d


    Unity Technologies

    Oct 1, 2015
    Hi, There's a sample in the ccd management package that shows how to do this. Basically, as noted above you have to be sure you login to Unity Services before you start using the API:

    It's in: