diff --git a/.github/workflows/zz-integration-test.yml b/.github/workflows/zz-integration-test.yml index 8c114fae..a4ff0d90 100644 --- a/.github/workflows/zz-integration-test.yml +++ b/.github/workflows/zz-integration-test.yml @@ -791,35 +791,6 @@ jobs: test "${{ steps.deploy.outputs.exit-code }}" = "1" test "${{ steps.deploy.outputs.drift-detected }}" = "true" - state-deploy-community: - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Setup Flyway - uses: red-gate/setup-flyway@v3 - with: - edition: community - i-agree-to-the-eula: true - - name: Run state deploy - id: deploy - uses: ./state/deploy - with: - target-url: jdbc:sqlite:state-target.db - script-path: deployments/D__deployment.sql - working-directory: sample-projects/sqlite - - name: Test outputs - shell: bash - run: | - set -euo pipefail - echo "Exit code: ${{ steps.deploy.outputs.exit-code }}" - echo "Drift detected: ${{ steps.deploy.outputs.drift-detected }}" - test "${{ steps.deploy.outputs.exit-code }}" = "0" - test "${{ steps.deploy.outputs.drift-detected }}" = "" - state-deploy-no-comparison-support: strategy: matrix: diff --git a/state/deploy/dist/index.js b/state/deploy/dist/index.js index fc561b9a..1c8b09ae 100644 --- a/state/deploy/dist/index.js +++ b/state/deploy/dist/index.js @@ -10509,12 +10509,16 @@ await (async () => { rn("Flyway is not installed or not in PATH. Run red-gate/setup-flyway before this action."); return; } + if (e.edition !== "enterprise") { + rn(`State-based deployments require Flyway Enterprise edition (current edition: ${e.edition}).`); + return; + } let t = En(); if (!t.targetEnvironment && !t.targetUrl) { rn("Either \"target-environment\" or \"target-url\" must be provided for Flyway to connect to a database."); return; } - if (Dn(t), e.edition === "enterprise") if (t.skipDriftCheck) on("Skipping drift check: \"skip-drift-check\" set to true"), t.saveSnapshot = !0; + if (Dn(t), t.skipDriftCheck) on("Skipping drift check: \"skip-drift-check\" set to true"), t.saveSnapshot = !0; else { let { driftDetected: e, comparisonSupported: n } = await On(t); if (e) { @@ -10523,7 +10527,6 @@ await (async () => { } t.saveSnapshot = n; } - else on(`Skipping drift check as edition is not Enterprise (actual edition: ${e.edition}).`); await wn(t); } catch (e) { e instanceof Error ? rn(e.message) : rn(String(e)); diff --git a/state/deploy/src/main.ts b/state/deploy/src/main.ts index 0ddf8479..c228f08e 100644 --- a/state/deploy/src/main.ts +++ b/state/deploy/src/main.ts @@ -24,6 +24,12 @@ const run = async (): Promise => { core.setFailed("Flyway is not installed or not in PATH. Run red-gate/setup-flyway before this action."); return; } + if (flywayDetails.edition !== "enterprise") { + core.setFailed( + `State-based deployments require Flyway Enterprise edition (current edition: ${flywayDetails.edition}).`, + ); + return; + } const inputs = getInputs(); if (!inputs.targetEnvironment && !inputs.targetUrl) { core.setFailed( @@ -34,20 +40,16 @@ const run = async (): Promise => { maskSecrets(inputs); - if (flywayDetails.edition === "enterprise") { - if (inputs.skipDriftCheck) { - core.info('Skipping drift check: "skip-drift-check" set to true'); - inputs.saveSnapshot = true; - } else { - const { driftDetected, comparisonSupported } = await checkForDrift(inputs); - if (driftDetected) { - core.setFailed("Drift detected. Aborting deployment."); - return; - } - inputs.saveSnapshot = comparisonSupported; - } + if (inputs.skipDriftCheck) { + core.info('Skipping drift check: "skip-drift-check" set to true'); + inputs.saveSnapshot = true; } else { - core.info(`Skipping drift check as edition is not Enterprise (actual edition: ${flywayDetails.edition}).`); + const { driftDetected, comparisonSupported } = await checkForDrift(inputs); + if (driftDetected) { + core.setFailed("Drift detected. Aborting deployment."); + return; + } + inputs.saveSnapshot = comparisonSupported; } await deploy(inputs); diff --git a/state/deploy/tests/main.test.ts b/state/deploy/tests/main.test.ts index 26ab7458..ab57e188 100644 --- a/state/deploy/tests/main.test.ts +++ b/state/deploy/tests/main.test.ts @@ -71,8 +71,17 @@ describe("run", () => { expect(setFailed).toHaveBeenCalledWith(expect.stringContaining("Flyway is not installed")); }); - it("should fail when neither url nor environment is provided", async () => { + it("should fail for non-enterprise edition", async () => { setupFlywayMock({ edition: "Community", deployExitCode: 0 }); + + await import("../src/main.js"); + await vi.dynamicImportSettled(); + + expect(setFailed).toHaveBeenCalledWith(expect.stringContaining("require Flyway Enterprise edition")); + }); + + it("should fail when neither url nor environment is provided", async () => { + setupFlywayMock({ edition: "Enterprise", driftExitCode: 0, deployExitCode: 0 }); getInput.mockReturnValue(""); await import("../src/main.js"); @@ -106,30 +115,8 @@ describe("run", () => { ); }); - it("should not include saveSnapshot for community edition", async () => { - setupFlywayMock({ - edition: "Community", - deployExitCode: 0, - }); - getInput.mockImplementation((name: string) => { - if (name === "target-url") { - return "jdbc:sqlite:test.db"; - } - return ""; - }); - - await import("../src/main.js"); - await vi.dynamicImportSettled(); - - expect(exec).not.toHaveBeenCalledWith( - "flyway", - expect.arrayContaining(["-deploy.saveSnapshot=true"]), - expect.any(Object), - ); - }); - it("should fail when flyway returns non-zero exit code", async () => { - setupFlywayMock({ edition: "Community", deployExitCode: 1 }); + setupFlywayMock({ edition: "Enterprise", driftExitCode: 0, deployExitCode: 1 }); getInput.mockImplementation((name: string) => { if (name === "target-url") { return "jdbc:sqlite:test.db"; @@ -145,7 +132,8 @@ describe("run", () => { it("should set outputs on successful execution", async () => { setupFlywayMock({ - edition: "Community", + edition: "Enterprise", + driftExitCode: 0, deployExitCode: 0, }); getInput.mockImplementation((name: string) => { diff --git a/state/prepare/dist/index.js b/state/prepare/dist/index.js index a333ddc8..59205336 100644 --- a/state/prepare/dist/index.js +++ b/state/prepare/dist/index.js @@ -10630,12 +10630,16 @@ await (async () => { rn("Flyway is not installed or not in PATH. Run red-gate/setup-flyway before this action."); return; } + if (e.edition !== "enterprise") { + rn(`State-based deployments require Flyway Enterprise edition (current edition: ${e.edition}).`); + return; + } let t = Hn(); if (!t.targetEnvironment && !t.targetUrl) { rn("Either \"target-environment\" or \"target-url\" must be provided for Flyway to connect to a database."); return; } - if (Un(t), e.edition === "enterprise") if (t.skipDriftCheck) sn("Skipping drift check: \"skip-drift-check\" set to true"); + if (Un(t), t.skipDriftCheck) sn("Skipping drift check: \"skip-drift-check\" set to true"); else { let { driftDetected: e } = await Wn(t); if (e) { @@ -10646,7 +10650,6 @@ await (async () => { on("Drift detected. Continuing because fail-on-drift is disabled."); } } - else sn(`Skipping drift check as edition is not Enterprise (actual edition: ${e.edition}).`); await jn(t, e.edition); let { scriptPath: n } = await Bn(t); if (!n) { diff --git a/state/prepare/src/main.ts b/state/prepare/src/main.ts index 9f94d414..6741f785 100644 --- a/state/prepare/src/main.ts +++ b/state/prepare/src/main.ts @@ -26,6 +26,12 @@ const run = async (): Promise => { core.setFailed("Flyway is not installed or not in PATH. Run red-gate/setup-flyway before this action."); return; } + if (flywayDetails.edition !== "enterprise") { + core.setFailed( + `State-based deployments require Flyway Enterprise edition (current edition: ${flywayDetails.edition}).`, + ); + return; + } const inputs = getInputs(); if (!inputs.targetEnvironment && !inputs.targetUrl) { core.setFailed( @@ -36,21 +42,17 @@ const run = async (): Promise => { maskSecrets(inputs); - if (flywayDetails.edition === "enterprise") { - if (inputs.skipDriftCheck) { - core.info('Skipping drift check: "skip-drift-check" set to true'); - } else { - const { driftDetected } = await checkForDrift(inputs); - if (driftDetected) { - if (inputs.failOnDrift) { - core.setFailed("Drift detected. Aborting prepare."); - return; - } - core.warning("Drift detected. Continuing because fail-on-drift is disabled."); + if (inputs.skipDriftCheck) { + core.info('Skipping drift check: "skip-drift-check" set to true'); + } else { + const { driftDetected } = await checkForDrift(inputs); + if (driftDetected) { + if (inputs.failOnDrift) { + core.setFailed("Drift detected. Aborting prepare."); + return; } + core.warning("Drift detected. Continuing because fail-on-drift is disabled."); } - } else { - core.info(`Skipping drift check as edition is not Enterprise (actual edition: ${flywayDetails.edition}).`); } await runCheckChanges(inputs, flywayDetails.edition); diff --git a/state/prepare/tests/main.test.ts b/state/prepare/tests/main.test.ts index cb7aeff1..2159755a 100644 --- a/state/prepare/tests/main.test.ts +++ b/state/prepare/tests/main.test.ts @@ -97,8 +97,17 @@ describe("run", () => { expect(setFailed).toHaveBeenCalledWith(expect.stringContaining("Flyway is not installed")); }); - it("should fail when neither url nor environment is provided", async () => { + it("should fail for non-enterprise edition", async () => { setupFlywayMock({ edition: "Community", prepareExitCode: 0 }); + + await import("../src/main.js"); + await vi.dynamicImportSettled(); + + expect(setFailed).toHaveBeenCalledWith(expect.stringContaining("require Flyway Enterprise edition")); + }); + + it("should fail when neither url nor environment is provided", async () => { + setupFlywayMock({ edition: "Enterprise", driftExitCode: 0, prepareExitCode: 0 }); getInput.mockReturnValue(""); await import("../src/main.js"); @@ -221,24 +230,6 @@ describe("run", () => { expect(setFailed).not.toHaveBeenCalled(); }); - it("should skip drift check for community edition", async () => { - setupFlywayMock({ - edition: "Community", - codeReviewExitCode: 0, - prepareExitCode: 0, - prepareOutput: { scriptFilename: "deployments/D__deployment.sql" }, - }); - getInput.mockImplementation((name: string) => (name === "target-url" ? "jdbc:sqlite:test.db" : "")); - - await import("../src/main.js"); - await vi.dynamicImportSettled(); - - expect(getDriftCheckCalls()).toHaveLength(0); - expect(getPrepareCalls()).toHaveLength(1); - expect(info).toHaveBeenCalledWith(expect.stringContaining("edition is not Enterprise")); - expect(setFailed).not.toHaveBeenCalled(); - }); - it("should proceed with prepare when no drift detected for enterprise edition", async () => { setupFlywayMock({ edition: "Enterprise", @@ -336,7 +327,8 @@ describe("run", () => { it("should call runCheckChanges", async () => { setupFlywayMock({ - edition: "Community", + edition: "Enterprise", + driftExitCode: 0, codeReviewExitCode: 0, prepareExitCode: 0, prepareOutput: { scriptFilename: "deployments/D__deployment.sql" }, @@ -346,7 +338,7 @@ describe("run", () => { await import("../src/main.js"); await vi.dynamicImportSettled(); - expect(runCheckChanges).toHaveBeenCalledWith(expect.any(Object), "community"); + expect(runCheckChanges).toHaveBeenCalledWith(expect.any(Object), "enterprise"); }); it("should skip code review when skip-code-review is enabled", async () => {