Search code examples
javac#.netgithub-actionsliquibase

"Error: Unable to access jarfile" in GitHub Actions when executing via .NET, but works from command line


I have a database with its schema managed using Liquibase. I have a .NET application that uses that database for persistence. I have integration tests that use Testcontainers. So when those tests run each test suite gets its own MySQL database, via Docker, to run its tests against and then those databases get terminated.

Liquibase does not have a native integration with .NET, so to run the database migrations I am calling out to the Liquibase CLI from .NET after the Testcontainers are available. This works perfectly locally, but I am having issues when I try to put this together in GitHub Actions:

jobs:
  build-app:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
    - name: Configure AWS CLI
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-2
    - name: Checkout
      uses: actions/checkout@v4
    - name: Setup .NET Core
      uses: actions/[email protected]
      with:
        dotnet-version: 6.0.x
    - name: Setup Java
      uses: actions/setup-java@v2
      with:
        distribution: 'adopt'
        java-version: '11'
    - name: Install Liquibase
      run: |
        mkdir liquibase
        chmod +w liquibase
        wget https://github.com/liquibase/liquibase/releases/download/v4.26.0/liquibase-4.26.0.tar.gz
        tar -xzf liquibase-4.26.0.tar.gz -C liquibase
        echo "LIQUIBASE_HOME=${GITHUB_WORKSPACE}/liquibase/liquibase" >> $GITHUB_ENV
        
        chmod -R 755 liquibase
        
        liquibase/liquibase --version
    - name: Login to AWS CodeArtifact
      run: |
          aws codeartifact login --tool dotnet --repository ${{ secrets.AWS_CODEARTIFACT_REPOSITORY }} --domain ${{ secrets.AWS_CODEARTIFACT_DOMAIN }} --domain-owner ${{ secrets.AWS_CODEARTIFACT_ACCOUNT_NUMBER }}
    - name: Restore
      uses: cake-build/cake-action@v2
      with:
        target: Restore
    - name: Build
      uses: cake-build/cake-action@v2
      with:
        target: Build-CI
        arguments: |
          versionNumber: ${{inputs.version}}
    - name: Configure AWS CLI
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ secrets.INTEGRATION_RUNNER_AWS_ACCESS_KEY }}
        aws-secret-access-key: ${{ secrets.INTEGRATION_RUNNER_AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-2
        role-to-assume: ${{ secrets.INTEGRATION_RUNNER_ROLE_ARN }}
    - name: Run tests
      uses: cake-build/cake-action@v2
      with:
        target: Test-CI

The call to liquibase/liquibase --version in the Install Liquibase step runs well, so I know Liquibase is installed correctly. However, once I call out to Liquibase from .NET I get the following error:

Error: Unable to access jarfile /home/runner/work/MyProject/WorkingDirectory/liquibase/liquibase/internal/lib/liquibase-core.jar

This file exists, so it is not a path issue. The .NET code that is doing that is:

private void RunLiquibaseChangesets()
    {
        const string changelog = "db/root-changelog.xml";
        var (username, password, databaseName) = DeconstructConnectionString(_dbContainer.GetConnectionString());
        var url = $"jdbc:mysql://localhost:{_dbContainer.GetMappedPublicPort(ContainerPortNumber)}/{databaseName}";
        var arguments =
            $"update --url={url} --username={username} --password={password} --changeLogFile={changelog} --contexts=test-data";
        var fileName = Environment.GetEnvironmentVariable("LIQUIBASE_HOME") ?? throw new InvalidOperationException(
            "LIQUIBASE_HOME environment variable not set. Please set it to the liquibase installation directory.");
        var rootDirectory = Environment.GetEnvironmentVariable("GITHUB_WORKSPACE") ??
                            Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..");

        var startInfo = new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            WorkingDirectory = rootDirectory,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };

        using var process = new Process();
        process.StartInfo = startInfo;
        process.Start();

        // Use StringBuilder to capture output
        var outputBuilder = new StringBuilder();
        var errorBuilder = new StringBuilder();

        // Handle the output events
        process.OutputDataReceived += (_, args) => outputBuilder.AppendLine(args.Data);
        process.ErrorDataReceived += (_, args) => errorBuilder.AppendLine(args.Data);

        // Start reading output and error asynchronously
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        var exited = process.WaitForExit(10000);
        
        Console.WriteLine("----------------- Liquibase Output Start -----------------");
        Console.WriteLine(outputBuilder.ToString());
        Console.WriteLine("----------------- Liquibase Output End -----------------");
        
        Console.WriteLine("----------------- Liquibase Error Start -----------------");
        Console.WriteLine(errorBuilder.ToString());
        Console.WriteLine("----------------- Liquibase Error End -----------------");

        // Check to see if the process has exited.
        if (!exited)
        {
            throw new InvalidOperationException($"Process did not exit: Output: {outputBuilder} Error: {errorBuilder}");
        }
    }

I suspect it is permissions, but I have given full permissions via chmod so I am stuck as to what to try next.


Solution

  • So it turns out that LIQUIBASE_HOME has some special meaning to Liquibase even though it is not listed anywhere I can find. So I resolved this by using LIQUIBASE_INSTALL_DIR instead.

    Thanks to @jonrsharpe for nudging me in the right direction.