From d6c5edf96374d1a4433af850abbbf7decdcfa774 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:00:26 +0100 Subject: [PATCH 01/18] chore: remove unused files --- src/ffi/internals/index.spec.ts | 34 ------------------ src/ffi/internals/index.ts | 63 --------------------------------- 2 files changed, 97 deletions(-) delete mode 100644 src/ffi/internals/index.spec.ts delete mode 100644 src/ffi/internals/index.ts diff --git a/src/ffi/internals/index.spec.ts b/src/ffi/internals/index.spec.ts deleted file mode 100644 index b3a1c2dc..00000000 --- a/src/ffi/internals/index.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import chai = require('chai'); -import chaiAsPromised = require('chai-as-promised'); -import { libName } from '.'; - -const { expect } = chai; -chai.use(chaiAsPromised); - -describe('ffi names', () => { - it('has the correct name for windows', () => { - expect(libName('pact_ffi', 'v0.0.1', 'x64', 'win32')).to.be.equal( - 'v0.0.1-pact_ffi-windows-x86_64.dll' - ); - }); - it('has the correct name for linux intel', () => { - expect(libName('pact_ffi', 'v0.0.1', 'x64', 'linux')).to.be.equal( - 'v0.0.1-libpact_ffi-linux-x86_64.so' - ); - }); - it('has the correct name for linux arm', () => { - expect(libName('pact_ffi', 'v0.0.1', 'arm64', 'linux')).to.be.equal( - 'v0.0.1-libpact_ffi-linux-aarch64.so' - ); - }); - it('has the correct name for osx intel', () => { - expect(libName('pact_ffi', 'v0.0.1', 'x64', 'darwin')).to.be.equal( - 'v0.0.1-libpact_ffi-osx-x86_64.dylib' - ); - }); - it('has the correct name for osx arm', () => { - expect(libName('pact_ffi', 'v0.0.1', 'arm64', 'darwin')).to.be.equal( - 'v0.0.1-libpact_ffi-osx-aarch64.dylib' - ); - }); -}); diff --git a/src/ffi/internals/index.ts b/src/ffi/internals/index.ts deleted file mode 100644 index 0b34f6d2..00000000 --- a/src/ffi/internals/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -// This is a lookup between process.platform and -// the platform names used in pact-reference -const PLATFORM_LOOKUP = { - linux: 'linux', - darwin: 'osx', - win32: 'windows', // yes, 'win32' is what process.platform returns on windows 64 bit -}; - -// This is a lookup between process.platform and -// the prefixes for the library name -const LIBNAME_PREFIX_LOOKUP = { - linux: 'lib', - darwin: 'lib', - win32: '', // yes, 'win32' is what process.platform returns on windows 64 bit -}; - -// This is a lookup between process.arch and -// the architecture names used in pact-reference -const ARCH_LOOKUP = { x64: 'x86_64', arm64: 'aarch64' }; - -// This is a lookup between "${platform}-${arch}" and -// the file extensions to link on that platform/arch combination -const EXTENSION_LOOKUP = { - 'osx-x86_64': 'dylib', - 'osx-aarch64': 'dylib', - 'linux-x86_64': 'so', - 'linux-aarch64': 'so', - 'windows-x86_64': 'dll', -}; - -export const libName = ( - library: string, - version: string, - processArch = process.arch, - processPlatform = process.platform -): string => { - const arch = ARCH_LOOKUP[processArch]; - const platform = PLATFORM_LOOKUP[processPlatform]; - - if (!arch || !platform) { - throw new Error( - `Pact does not currently support the operating system and architecture combination '${processPlatform}/${processArch}'` - ); - } - - const target = `${platform}-${arch}`; - - const extension = EXTENSION_LOOKUP[target]; - if (!extension) { - throw new Error( - `Pact doesn't know what extension to use for the libraries in the architecture combination '${target}'` - ); - } - - const libnamePrefix = LIBNAME_PREFIX_LOOKUP[processPlatform]; - if (libnamePrefix === undefined) { - throw new Error( - `Pact doesn't know what prefix to use for the libraries on '${processPlatform}'` - ); - } - - return `${version}-${libnamePrefix}${library}-${target}.${extension}`; -}; From 24f9b42a8cc8414bbcf599ce3a006042b0ae5009 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:01:07 +0100 Subject: [PATCH 02/18] chore: prebuild, only download ffi libs --- script/ci/prebuild.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/ci/prebuild.sh b/script/ci/prebuild.sh index c3c5b0a3..70d65184 100755 --- a/script/ci/prebuild.sh +++ b/script/ci/prebuild.sh @@ -34,7 +34,8 @@ npm --version echo "OS: $OS" echo "ARCH: $ARCH" -./script/download-libs.sh +. "${SCRIPT_DIR}/../lib/export-binary-versions.sh" +"${SCRIPT_DIR}/../lib/download-ffi.sh" npm ci --ignore-scripts export npm_config_target=${NODE_VERSION} npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --name ${PREBUILD_NAME} From 50d58adece64e44f398b6d4a379e9dcdcebd149f Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:02:17 +0100 Subject: [PATCH 03/18] feat: add musl support, for x86_64/aarch64 --- binding.gyp | 313 +++++++++++++++++--------------- script/ci/check-release-libs.sh | 2 + script/ci/prebuild-alpine.sh | 34 ++++ script/lib/download-ffi.sh | 21 ++- 4 files changed, 217 insertions(+), 153 deletions(-) create mode 100755 script/ci/prebuild-alpine.sh diff --git a/binding.gyp b/binding.gyp index bb0c7998..e041dde8 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,150 +1,173 @@ { - "targets": [ - { - "target_name": "pact", - "sources": [ - "native/addon.cc", - "native/ffi.cc", - "native/consumer.cc", - "native/provider.cc", - "native/plugin.cc" - ], - "include_dirs": [ - " Date: Sat, 11 May 2024 22:03:34 +0100 Subject: [PATCH 04/18] test: allow conditional skipping of pact ruby standalone tests via SKIP_STANDALONE_TESTS env var --- script/download-standalone.sh | 4 +- src/can-deploy/can-deploy.spec.ts | 14 +- src/message.spec.ts | 3 +- src/pact-standalone.spec.ts | 224 +++++++++++++++-------------- src/pact.spec.ts | 3 +- src/publisher.spec.ts | 3 +- src/server.spec.ts | 3 +- src/stub.spec.ts | 3 +- test/publisher.integration.spec.ts | 3 +- 9 files changed, 140 insertions(+), 120 deletions(-) diff --git a/script/download-standalone.sh b/script/download-standalone.sh index 44dff19b..591ef34a 100755 --- a/script/download-standalone.sh +++ b/script/download-standalone.sh @@ -2,4 +2,6 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)" # Figure out where the script is running . "${SCRIPT_DIR}/lib/export-binary-versions.sh" -"${SCRIPT_DIR}/lib/download-standalone.sh" \ No newline at end of file +if [ "${SKIP_STANDALONE_TESTS:-"false"}" != "true" ]; then + "${SCRIPT_DIR}/lib/download-standalone.sh" +fi diff --git a/src/can-deploy/can-deploy.spec.ts b/src/can-deploy/can-deploy.spec.ts index 3659c961..89de291d 100644 --- a/src/can-deploy/can-deploy.spec.ts +++ b/src/can-deploy/can-deploy.spec.ts @@ -13,8 +13,8 @@ import brokerMock from '../../test/integration/broker-mock'; const { expect } = chai; chai.use(chaiAsPromised); - -describe('CanDeploy Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('CanDeploy Spec', () => { const PORT = Math.floor(Math.random() * 999) + 9000; let server: http.Server; let absolutePath: string; @@ -131,6 +131,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'Foo', version: '4' }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -145,6 +146,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'Foo', latest: true }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -158,6 +160,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'FooFail', latest: true }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -173,6 +176,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'Foo', latest: 'tag' }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -186,6 +190,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'FooFail', latest: 'tag' }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -202,6 +207,7 @@ describe('CanDeploy Spec', () => { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'Foo', latest: 'tag' }], to: 'prod', + output: 'table', }; const ding = canDeployFactory(opts); @@ -216,6 +222,7 @@ describe('CanDeploy Spec', () => { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'FooFail', latest: 'tag' }], to: 'prod', + output: 'table', }; const ding = canDeployFactory(opts); @@ -230,6 +237,7 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'FooFail', version: '4' }], + output: 'table', }; const ding = canDeployFactory(opts); @@ -243,7 +251,6 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'Foo', version: '4' }], - output: 'json', }; const ding = canDeployFactory(opts); @@ -257,7 +264,6 @@ describe('CanDeploy Spec', () => { const opts: CanDeployOptions = { pactBroker: `http://localhost:${PORT}`, pacticipants: [{ name: 'FooFail', version: '4' }], - output: 'json', }; const ding = canDeployFactory(opts); diff --git a/src/message.spec.ts b/src/message.spec.ts index 59b6d9e4..48b699fb 100644 --- a/src/message.spec.ts +++ b/src/message.spec.ts @@ -9,7 +9,8 @@ import messageFactory from './message'; const { expect } = chai; chai.use(chaiAsPromised); -describe('Message Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Message Spec', () => { const validJSON = `{ "description": "a test mesage", "content": { "name": "Mary" } }`; let absolutePath: string; diff --git a/src/pact-standalone.spec.ts b/src/pact-standalone.spec.ts index 9cee72eb..0be1d8b2 100644 --- a/src/pact-standalone.spec.ts +++ b/src/pact-standalone.spec.ts @@ -7,129 +7,135 @@ import { PactStandalone, standalone } from './pact-standalone'; const { expect } = chai; const basePath = pactEnvironment.cwd; +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; // Needs to stay a function and not an arrow function to access mocha 'this' context -describe('Pact Standalone', function forMocha() { - // Set timeout to 10 minutes because downloading binaries might take a while. - this.timeout(600000); - - let pact: PactStandalone; - - it('should return an object with cwd, file and fullPath properties that is platform specific', () => { - pact = standalone(); - expect(pact).to.be.an('object'); - expect(pact.cwd).to.be.ok; - expect(pact.brokerPath).to.contain('pact-broker'); - expect(pact.brokerFullPath).to.contain('pact-broker'); - expect(pact.mockServicePath).to.contain('pact-mock-service'); - expect(pact.mockServiceFullPath).to.contain('pact-mock-service'); - expect(pact.stubPath).to.contain('pact-stub-service'); - expect(pact.stubFullPath).to.contain('pact-stub-service'); - expect(pact.verifierPath).to.contain('pact-provider-verifier'); - expect(pact.verifierFullPath).to.contain('pact-provider-verifier'); - expect(pact.pactPath).to.contain('pact'); - expect(pact.pactFullPath).to.contain('pact'); - expect(pact.pactflowPath).to.contain('pactflow'); - expect(pact.pactflowFullPath).to.contain('pactflow'); - }); - - it("should return the base directory of the project with 'cwd' (where the package.json file is)", () => { - expect(fs.existsSync(path.resolve(pact.cwd, 'package.json'))).to.be.true; - }); - - describe('Check if OS specific files are there', () => { - const tests = [ - ['darwin', 'arm64'], - ['darwin', 'x64'], - ['linux', 'arm64'], - ['linux', 'x64'], - ['win32', 'x64'], - ].filter(([platform]) => - process.env['ONLY_DOWNLOAD_PACT_FOR_WINDOWS'] - ? platform === 'win32' - : true - ); - - tests.forEach(([platform, arch]) => { - describe(`${platform} ${arch}`, () => { - beforeEach(() => { - pact = standalone(platform, arch); - }); +(skipStandaloneTests ? describe.skip : describe)( + 'Pact Standalone', + function forMocha() { + // Set timeout to 10 minutes because downloading binaries might take a while. + this.timeout(600000); + + let pact: PactStandalone; + + it('should return an object with cwd, file and fullPath properties that is platform specific', () => { + pact = standalone(); + expect(pact).to.be.an('object'); + expect(pact.cwd).to.be.ok; + expect(pact.brokerPath).to.contain('pact-broker'); + expect(pact.brokerFullPath).to.contain('pact-broker'); + expect(pact.mockServicePath).to.contain('pact-mock-service'); + expect(pact.mockServiceFullPath).to.contain('pact-mock-service'); + expect(pact.stubPath).to.contain('pact-stub-service'); + expect(pact.stubFullPath).to.contain('pact-stub-service'); + expect(pact.verifierPath).to.contain('pact-provider-verifier'); + expect(pact.verifierFullPath).to.contain('pact-provider-verifier'); + expect(pact.pactPath).to.contain('pact'); + expect(pact.pactFullPath).to.contain('pact'); + expect(pact.pactflowPath).to.contain('pactflow'); + expect(pact.pactflowFullPath).to.contain('pactflow'); + }); - it('broker relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.brokerPath))).to.be - .true; - }); + it("should return the base directory of the project with 'cwd' (where the package.json file is)", () => { + expect(fs.existsSync(path.resolve(pact.cwd, 'package.json'))).to.be.true; + }); - it('broker full path', () => { - expect(fs.existsSync(pact.brokerFullPath)).to.be.true; - }); + describe('Check if OS specific files are there', () => { + const tests = [ + ['darwin', 'arm64'], + ['darwin', 'x64'], + ['linux', 'arm64'], + ['linux', 'x64'], + ['win32', 'x64'], + ].filter(([platform]) => + process.env['ONLY_DOWNLOAD_PACT_FOR_WINDOWS'] + ? platform === 'win32' + : true + ); + + tests.forEach(([platform, arch]) => { + describe(`${platform} ${arch}`, () => { + beforeEach(() => { + pact = standalone(platform, arch); + }); - it('mock service relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.mockServicePath))).to - .be.true; - }); + it('broker relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.brokerPath))).to.be + .true; + }); - it('mock service full path', () => { - expect(fs.existsSync(pact.mockServiceFullPath)).to.be.true; - }); + it('broker full path', () => { + expect(fs.existsSync(pact.brokerFullPath)).to.be.true; + }); - it('stub relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.stubPath))).to.be - .true; - }); + it('mock service relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.mockServicePath))) + .to.be.true; + }); - it('stub full path', () => { - expect(fs.existsSync(pact.stubFullPath)).to.be.true; - }); + it('mock service full path', () => { + expect(fs.existsSync(pact.mockServiceFullPath)).to.be.true; + }); - it('provider verifier relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.verifierPath))).to.be - .true; - }); + it('stub relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.stubPath))).to.be + .true; + }); - it('provider verifier full path', () => { - expect(fs.existsSync(pact.verifierFullPath)).to.be.true; - }); + it('stub full path', () => { + expect(fs.existsSync(pact.stubFullPath)).to.be.true; + }); - it('pact relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.pactPath))).to.be - .true; - }); + it('provider verifier relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.verifierPath))).to + .be.true; + }); - it('pact full path', () => { - expect(fs.existsSync(pact.pactFullPath)).to.be.true; - }); + it('provider verifier full path', () => { + expect(fs.existsSync(pact.verifierFullPath)).to.be.true; + }); - it('pactflow relative path', () => { - expect(fs.existsSync(path.resolve(basePath, pact.pactflowPath))).to.be - .true; - }); + it('pact relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.pactPath))).to.be + .true; + }); - it('pactflow full path', () => { - expect(fs.existsSync(pact.pactflowFullPath)).to.be.true; - }); + it('pact full path', () => { + expect(fs.existsSync(pact.pactFullPath)).to.be.true; + }); - if (platform === 'win32') { - it("should add '.bat' to the end of the binary names", () => { - expect(pact.brokerPath).to.contain('pact-broker.bat'); - expect(pact.brokerFullPath).to.contain('pact-broker.bat'); - expect(pact.mockServicePath).to.contain('pact-mock-service.bat'); - expect(pact.mockServiceFullPath).to.contain( - 'pact-mock-service.bat' - ); - expect(pact.stubPath).to.contain('pact-stub-service.bat'); - expect(pact.stubFullPath).to.contain('pact-stub-service.bat'); - expect(pact.verifierPath).to.contain('pact-provider-verifier.bat'); - expect(pact.verifierFullPath).to.contain( - 'pact-provider-verifier.bat' - ); - expect(pact.pactPath).to.contain('pact.bat'); - expect(pact.pactFullPath).to.contain('pact.bat'); - expect(pact.pactflowPath).to.contain('pactflow.bat'); - expect(pact.pactflowFullPath).to.contain('pactflow.bat'); + it('pactflow relative path', () => { + expect(fs.existsSync(path.resolve(basePath, pact.pactflowPath))).to + .be.true; }); - } + + it('pactflow full path', () => { + expect(fs.existsSync(pact.pactflowFullPath)).to.be.true; + }); + + if (platform === 'win32') { + it("should add '.bat' to the end of the binary names", () => { + expect(pact.brokerPath).to.contain('pact-broker.bat'); + expect(pact.brokerFullPath).to.contain('pact-broker.bat'); + expect(pact.mockServicePath).to.contain('pact-mock-service.bat'); + expect(pact.mockServiceFullPath).to.contain( + 'pact-mock-service.bat' + ); + expect(pact.stubPath).to.contain('pact-stub-service.bat'); + expect(pact.stubFullPath).to.contain('pact-stub-service.bat'); + expect(pact.verifierPath).to.contain( + 'pact-provider-verifier.bat' + ); + expect(pact.verifierFullPath).to.contain( + 'pact-provider-verifier.bat' + ); + expect(pact.pactPath).to.contain('pact.bat'); + expect(pact.pactFullPath).to.contain('pact.bat'); + expect(pact.pactflowPath).to.contain('pactflow.bat'); + expect(pact.pactflowFullPath).to.contain('pactflow.bat'); + }); + } + }); }); }); - }); -}); + } +); diff --git a/src/pact.spec.ts b/src/pact.spec.ts index 9224bfd5..f495e9c7 100644 --- a/src/pact.spec.ts +++ b/src/pact.spec.ts @@ -8,7 +8,8 @@ import { ServerOptions } from '.'; const { expect } = chai; chai.use(chaiAsPromised); -describe('Pact Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Pact Spec', () => { afterEach(() => pact.removeAllServers()); describe('Set Log Level', () => { diff --git a/src/publisher.spec.ts b/src/publisher.spec.ts index dd1b78ec..ae8144a9 100644 --- a/src/publisher.spec.ts +++ b/src/publisher.spec.ts @@ -13,7 +13,8 @@ import { PublisherOptions } from './types'; const { expect } = chai; chai.use(chaiAsPromised); -describe('Publish Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Publish Spec', () => { const PORT = Math.floor(Math.random() * 999) + 9000; const pactFile = path.resolve( __dirname, diff --git a/src/server.spec.ts b/src/server.spec.ts index 3a6a8d12..4f348476 100644 --- a/src/server.spec.ts +++ b/src/server.spec.ts @@ -11,7 +11,8 @@ chai.use(chaiAsPromised); const { expect } = chai; const rm = util.promisify(rimraf); -describe('Server Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Server Spec', () => { let server: any; const monkeypatchFile: string = path.resolve( __dirname, diff --git a/src/stub.spec.ts b/src/stub.spec.ts index dbb52b3e..9f4207b5 100644 --- a/src/stub.spec.ts +++ b/src/stub.spec.ts @@ -7,7 +7,8 @@ import stubFactory from './stub'; chai.use(chaiAsPromised); const { expect } = chai; -describe('Stub Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Stub Spec', () => { let stub: any; const validDefaults = { pactUrls: [ diff --git a/test/publisher.integration.spec.ts b/test/publisher.integration.spec.ts index c9efb13e..62fbb7fb 100644 --- a/test/publisher.integration.spec.ts +++ b/test/publisher.integration.spec.ts @@ -9,7 +9,8 @@ import brokerMock from './integration/broker-mock'; const { expect } = chai; chai.use(chaiAsPromised); -describe('Publish Spec', () => { +const skipStandaloneTests = process.env['SKIP_STANDALONE_TESTS'] === 'true'; +(skipStandaloneTests ? describe.skip : describe)('Publish Spec', () => { let server: http.Server; const PORT = Math.floor(Math.random() * 999) + 9000; const pactBrokerBaseUrl = `http://localhost:${PORT}`; From 7522969cfef3af52cd48f751916c22c885d26fd1 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:04:21 +0100 Subject: [PATCH 05/18] test: renable plugin tests --- script/ci/download-standalone-and-test.sh | 3 + script/download-libs.sh | 3 +- script/download-plugins.sh | 6 + script/install-plugin-cli.sh | 59 +++ script/lib/export-binary-versions.sh | 3 +- test/integration/grpc/grpc.json | 4 +- test/integration/plugin.proto | 421 ++++++++++++++++++++++ test/matt.consumer.integration.spec.ts | 9 +- test/matt.provider.integration.spec.ts | 3 +- test/plugin-verifier.integration.spec.ts | 68 ++-- test/verifier.integration.spec.ts | 3 +- 11 files changed, 539 insertions(+), 43 deletions(-) create mode 100755 script/download-plugins.sh create mode 100755 script/install-plugin-cli.sh create mode 100644 test/integration/plugin.proto diff --git a/script/ci/download-standalone-and-test.sh b/script/ci/download-standalone-and-test.sh index 65639d43..55affc12 100755 --- a/script/ci/download-standalone-and-test.sh +++ b/script/ci/download-standalone-and-test.sh @@ -6,4 +6,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)" # Figure out where the . "$SCRIPT_DIR"/../lib/robust-bash.sh ./script/download-standalone.sh +if [ "${SKIP_PLUGIN_TESTS:-"false"}" != "true" ]; then + ./script/download-plugins.sh +fi; ./script/ci/build-and-test.sh \ No newline at end of file diff --git a/script/download-libs.sh b/script/download-libs.sh index a94f09b5..53d68ef9 100755 --- a/script/download-libs.sh +++ b/script/download-libs.sh @@ -3,4 +3,5 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)" # Figure out where the . "${SCRIPT_DIR}/lib/export-binary-versions.sh" "${SCRIPT_DIR}/lib/download-ffi.sh" -"${SCRIPT_DIR}/lib/download-standalone.sh" \ No newline at end of file +"${SCRIPT_DIR}/lib/download-standalone.sh" +"${SCRIPT_DIR}/lib/download-plugins.sh" \ No newline at end of file diff --git a/script/download-plugins.sh b/script/download-plugins.sh new file mode 100755 index 00000000..76b850e3 --- /dev/null +++ b/script/download-plugins.sh @@ -0,0 +1,6 @@ +#!/bin/bash -eu +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)" # Figure out where the script is running + +. "${SCRIPT_DIR}/lib/export-binary-versions.sh" +"${SCRIPT_DIR}/install-plugin-cli.sh" +$HOME/.pact/bin/pact-plugin-cli install -y https://github.com/mefellows/pact-matt-plugin/releases/tag/$PACT_PLUGIN_MATT_VERSION \ No newline at end of file diff --git a/script/install-plugin-cli.sh b/script/install-plugin-cli.sh new file mode 100755 index 00000000..1947a329 --- /dev/null +++ b/script/install-plugin-cli.sh @@ -0,0 +1,59 @@ +#!/bin/sh -e +# +# Usage: +# $ curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-plugins/master/install-cli.sh | bash +# or +# $ wget -q https://raw.githubusercontent.com/pact-foundation/pact-plugins/master/install-cli.sh -O- | bash +# +set -e # Needed for Windows bash, which doesn't read the shebang + + + + + +detect_osarch() { + # detect_musl + case $(uname -sm) in + 'Linux x86_64') + os='linux' + arch='x86_64' + ;; + 'Linux aarch64') + os='linux' + arch='aarch64' + ;; + 'Darwin x86' | 'Darwin x86_64') + os='osx' + arch='x86_64' + ;; + 'Darwin arm64') + os='osx' + arch='aarch64' + ;; + CYGWIN*|MINGW32*|MSYS*|MINGW*) + os="windows" + arch='x86_64' + ext='.exe' + ;; + *) + echo "Sorry, you'll need to install the plugin CLI manually." + exit 1 + ;; + esac +} + + +VERSION="0.1.2" +detect_osarch + +if [ ! -f ~/.pact/bin/pact-plugin-cli ]; then + echo "--- 🐿 Installing plugins CLI version '${VERSION}' (from tag ${TAG})" + mkdir -p ~/.pact/bin + DOWNLOAD_LOCATION=https://github.com/pact-foundation/pact-plugins/releases/download/pact-plugin-cli-v${VERSION}/pact-plugin-cli-${os}-${arch}${ext}.gz + echo " Downloading from: ${DOWNLOAD_LOCATION}" + curl -L -o ~/.pact/bin/pact-plugin-cli-${os}-${arch}.gz "${DOWNLOAD_LOCATION}" + echo " Downloaded $(file ~/.pact/bin/pact-plugin-cli-${os}-${arch}.gz)" + gunzip -f ~/.pact/bin/pact-plugin-cli-${os}-${arch}.gz + mv ~/.pact/bin/pact-plugin-cli-${os}-${arch} ~/.pact/bin/pact-plugin-cli + chmod +x ~/.pact/bin/pact-plugin-cli +fi \ No newline at end of file diff --git a/script/lib/export-binary-versions.sh b/script/lib/export-binary-versions.sh index 066d2464..40d6e246 100644 --- a/script/lib/export-binary-versions.sh +++ b/script/lib/export-binary-versions.sh @@ -3,4 +3,5 @@ LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)" # Figure out where the scr PROJECT_DIR="${LIB_DIR}"/../../ export STANDALONE_VERSION=$(grep "PACT_STANDALONE_VERSION = '" "$PROJECT_DIR"/standalone/install.ts | grep -E -o "'(.*)'" | cut -d"'" -f2) -export FFI_VERSION=v$(grep "PACT_FFI_VERSION = '" "$PROJECT_DIR"/src/ffi/index.ts | grep -E -o "'(.*)'" | cut -d"'" -f2) \ No newline at end of file +export FFI_VERSION=v$(grep "PACT_FFI_VERSION = '" "$PROJECT_DIR"/src/ffi/index.ts | grep -E -o "'(.*)'" | cut -d"'" -f2) +export PACT_PLUGIN_MATT_VERSION=v0.1.1 \ No newline at end of file diff --git a/test/integration/grpc/grpc.json b/test/integration/grpc/grpc.json index 74850ce0..d11983f7 100644 --- a/test/integration/grpc/grpc.json +++ b/test/integration/grpc/grpc.json @@ -5,7 +5,6 @@ "interactions": [ { "description": "A request to do a foo", - "key": "539a26be10e0124e", "pending": false, "request": { "body": { @@ -47,7 +46,6 @@ "markup": "```protobuf\nmessage Feature {\n string name = 1;\n message .routeguide.Point location = 2;\n}\n```\n", "markupType": "COMMON_MARK" }, - "key": "d81a62841ce862db", "pending": false, "pluginConfiguration": { "protobuf": { @@ -143,7 +141,7 @@ } }, "name": "protobuf", - "version": "0.1.14" + "version": "0.3.15" } ] }, diff --git a/test/integration/plugin.proto b/test/integration/plugin.proto new file mode 100644 index 00000000..5ba54f1a --- /dev/null +++ b/test/integration/plugin.proto @@ -0,0 +1,421 @@ +// Proto file for Pact plugin interface V1 + +syntax = "proto3"; + +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/empty.proto"; + +package io.pact.plugin; +option go_package = "io.pact.plugin"; + +// Request to verify the plugin has loaded OK +message InitPluginRequest { + // Implementation calling the plugin + string implementation = 1; + // Version of the implementation + string version = 2; +} + +// Entry to be added to the core catalogue. Each entry describes one of the features the plugin provides. +// Entries will be stored in the catalogue under the key "plugin/$name/$type/$key". +message CatalogueEntry { + enum EntryType { + // Matcher for contents of messages, requests or response bodies + CONTENT_MATCHER = 0; + // Generator for contents of messages, requests or response bodies + CONTENT_GENERATOR = 1; + // Transport for a network protocol + TRANSPORT = 2; + // Matching rule for content field/values + MATCHER = 3; + // Type of interaction + INTERACTION = 4; + } + // Entry type + EntryType type = 1; + // Entry key + string key = 2; + // Associated data required for the entry. For CONTENT_MATCHER and CONTENT_GENERATOR types, a "content-types" + // value (separated by semi-colons) is required for all the content types the plugin supports. + map values = 3; +} + +// Response to init plugin, providing the catalogue entries the plugin provides +message InitPluginResponse { + // List of entries the plugin supports + repeated CatalogueEntry catalogue = 1; +} + +// Catalogue of Core Pact + Plugin features +message Catalogue { + // List of entries from the core catalogue + repeated CatalogueEntry catalogue = 1; +} + +// Message representing a request, response or message body +message Body { + // The content type of the body in MIME format (i.e. application/json) + string contentType = 1; + // Bytes of the actual content + google.protobuf.BytesValue content = 2; + // Enum of content type override. This is a hint on how the content type should be treated. + enum ContentTypeHint { + // Determine the form of the content using the default rules of the Pact implementation + DEFAULT = 0; + // Contents must always be treated as a text form + TEXT = 1; + // Contents must always be treated as a binary form + BINARY = 2; + } + // Content type override to apply (if required). If omitted, the default rules of the Pact implementation + // will be used + ContentTypeHint contentTypeHint = 3; +} + +// Request to preform a comparison on an actual body given the expected one +message CompareContentsRequest { + // Expected body from the Pact interaction + Body expected = 1; + // Actual received body + Body actual = 2; + // If unexpected keys or attributes should be allowed. Setting this to false results in additional keys or fields + // will cause a mismatch + bool allow_unexpected_keys = 3; + // Map of expressions to matching rules. The expressions follow the documented Pact matching rule expressions + map rules = 4; + // Additional data added to the Pact/Interaction by the plugin + PluginConfiguration pluginConfiguration = 5; +} + +// Indicates that there was a mismatch with the content type +message ContentTypeMismatch { + // Expected content type (MIME format) + string expected = 1; + // Actual content type received (MIME format) + string actual = 2; +} + +// A mismatch for an particular item of content +message ContentMismatch { + // Expected data bytes + google.protobuf.BytesValue expected = 1; + // Actual data bytes + google.protobuf.BytesValue actual = 2; + // Description of the mismatch + string mismatch = 3; + // Path to the item that was matched. This is the value as per the documented Pact matching rule expressions. + string path = 4; + // Optional diff of the contents + string diff = 5; + // Part of the interaction that the mismatch is for: body, headers, metadata, etc. + string mismatchType = 6; +} + +// List of content mismatches +message ContentMismatches { + repeated ContentMismatch mismatches = 1; +} + +// Response to the CompareContentsRequest with the results of the comparison +message CompareContentsResponse { + // Error message if an error occurred. If this field is set, the remaining fields will be ignored and the + // verification marked as failed + string error = 1; + // There was a mismatch with the types of content. If this is set, the results may not be set. + ContentTypeMismatch typeMismatch = 2; + // Results of the match, keyed by matching rule expression + map results = 3; +} + +// Request to configure/setup an interaction so that it can be verified later +message ConfigureInteractionRequest { + // Content type of the interaction (MIME format) + string contentType = 1; + // This is data specified by the user in the consumer test + google.protobuf.Struct contentsConfig = 2; +} + +// Represents a matching rule +message MatchingRule { + // Type of the matching rule + string type = 1; + // Associated data for the matching rule + google.protobuf.Struct values = 2; +} + +// List of matching rules +message MatchingRules { + repeated MatchingRule rule = 1; +} + +// Example generator +message Generator { + // Type of generator + string type = 1; + // Associated data for the generator + google.protobuf.Struct values = 2; +} + +// Plugin configuration added to the pact file by the ConfigureInteraction step +message PluginConfiguration { + // Data to be persisted against the interaction + google.protobuf.Struct interactionConfiguration = 1; + // Data to be persisted in the Pact file metadata (Global data) + google.protobuf.Struct pactConfiguration = 2; +} + +// Response to the configure/setup an interaction request +message InteractionResponse { + // Contents for the interaction + Body contents = 1; + // All matching rules to apply + map rules = 2; + // Generators to apply + map generators = 3; + // For message interactions, any metadata to be applied + google.protobuf.Struct messageMetadata = 4; + // Plugin specific data to be persisted in the pact file + PluginConfiguration pluginConfiguration = 5; + // Markdown/HTML formatted text representation of the interaction + string interactionMarkup = 6; + // Type of markup used + enum MarkupType { + // CommonMark format + COMMON_MARK = 0; + // HTML format + HTML = 1; + } + MarkupType interactionMarkupType = 7; + // Description of what part this interaction belongs to (in the case of there being more than one, for instance, + // request/response messages) + string partName = 8; + // All matching rules to apply to any message metadata + map metadata_rules = 9; + // Generators to apply to any message metadata + map metadata_generators = 10; +} + +// Response to the configure/setup an interaction request +message ConfigureInteractionResponse { + // If an error occurred. In this case, the other fields will be ignored/not set + string error = 1; + // The actual response if no error occurred. + repeated InteractionResponse interaction = 2; + // Plugin specific data to be persisted in the pact file + PluginConfiguration pluginConfiguration = 3; +} + +// Request to generate the contents using any defined generators +message GenerateContentRequest { + // Original contents + Body contents = 1; + // Generators to apply + map generators = 2; + // Additional data added to the Pact/Interaction by the plugin + PluginConfiguration pluginConfiguration = 3; + // Context data provided by the test framework + google.protobuf.Struct testContext = 4; + + // The mode of the generation, if running from a consumer test or during provider verification + enum TestMode { + Unknown = 0; + // Running on the consumer side + Consumer = 1; + // Running on the provider side + Provider = 2; + } + TestMode testMode = 5; + + // Which part the content is for + enum ContentFor { + Request = 0; + Response = 1; + } + ContentFor contentFor = 6; +} + +// Generated body/message response +message GenerateContentResponse { + Body contents = 1; +} + +// Request to start a mock server +message StartMockServerRequest { + // Interface to bind to. Will default to the loopback adapter + string hostInterface = 1; + // Port to bind to. Default (or a value of 0) get the OS to open a random port + uint32 port = 2; + // If TLS should be used (if supported by the mock server) + bool tls = 3; + // Pact as JSON to use for the mock server behaviour + string pact = 4; + // Context data provided by the test framework + google.protobuf.Struct testContext = 5; +} + +// Response to the start mock server request +message StartMockServerResponse { + oneof response { + // If an error occurred + string error = 1; + + // Mock server details + MockServerDetails details = 2; + } +} + +// Details on a running mock server +message MockServerDetails { + // Mock server unique ID + string key = 1; + // Port the mock server is running on + uint32 port = 2; + // IP address the mock server is bound to. Probably an IP6 address, but may be IP4 + string address = 3; +} + +// Request to shut down a running mock server +// TODO: replace this with MockServerRequest in the next major version +message ShutdownMockServerRequest { + // The server ID to shutdown + string serverKey = 1; +} + +// Request for a running mock server by ID +message MockServerRequest { + // The server ID to shutdown + string serverKey = 1; +} + +// Result of a request that the mock server received +message MockServerResult { + // service + method that was requested + string path = 1; + // If an error occurred trying to handle the request + string error = 2; + // Any mismatches that occurred + repeated ContentMismatch mismatches = 3; +} + +// Response to the shut down mock server request +// TODO: replace this with MockServerResults in the next major version +message ShutdownMockServerResponse { + // If the mock status is all ok + bool ok = 1; + // The results of the test run, will contain an entry for each request received by the mock server + repeated MockServerResult results = 2; +} + +// Matching results of the mock server. +message MockServerResults { + // If the mock status is all ok + bool ok = 1; + // The results of the test run, will contain an entry for each request received by the mock server + repeated MockServerResult results = 2; +} + +// Request to prepare an interaction for verification +message VerificationPreparationRequest { + // Pact as JSON to use for the verification + string pact = 1; + // Interaction key for the interaction from the Pact that is being verified + string interactionKey = 2; + // Any data supplied by the user to verify the interaction + google.protobuf.Struct config = 3; +} + +// Request metadata value. Will either be a JSON-like value, or binary data +message MetadataValue { + oneof value { + google.protobuf.Value nonBinaryValue = 1; + bytes binaryValue = 2; + } +} + +// Interaction request data to be sent or received for verification +message InteractionData { + // Request/Response body as bytes + Body body = 1; + // Metadata associated with the request/response + map metadata = 2; +} + +// Response for the prepare an interaction for verification request +message VerificationPreparationResponse { + oneof response { + // If an error occurred + string error = 1; + + // Interaction data required to construct any request + InteractionData interactionData = 2; + } +} + +// Request data to verify an interaction +message VerifyInteractionRequest { + // Interaction data required to construct the request + InteractionData interactionData = 1; + // Any data supplied by the user to verify the interaction + google.protobuf.Struct config = 2; + // Pact as JSON to use for the verification + string pact = 3; + // Interaction key for the interaction from the Pact that is being verified + string interactionKey = 4; +} + +message VerificationResultItem { + oneof result { + string error = 1; + ContentMismatch mismatch = 2; + } +} + +// Result of running the verification +message VerificationResult { + // Was the verification successful? + bool success = 1; + // Interaction data retrieved from the provider (optional) + InteractionData responseData = 2; + // Any mismatches that occurred + repeated VerificationResultItem mismatches = 3; + // Output for the verification to display to the user + repeated string output = 4; +} + +// Result of running the verification +message VerifyInteractionResponse { + oneof response { + // If an error occurred trying to run the verification + string error = 1; + + VerificationResult result = 2; + } +} + +service PactPlugin { + // Check that the plugin loaded OK. Returns the catalogue entries describing what the plugin provides + rpc InitPlugin(InitPluginRequest) returns (InitPluginResponse); + // Updated catalogue. This will be sent when the core catalogue has been updated (probably by a plugin loading). + rpc UpdateCatalogue(Catalogue) returns (google.protobuf.Empty); + // Request to perform a comparison of some contents (matching request) + rpc CompareContents(CompareContentsRequest) returns (CompareContentsResponse); + // Request to configure/setup the interaction for later verification. Data returned will be persisted in the pact file. + rpc ConfigureInteraction(ConfigureInteractionRequest) returns (ConfigureInteractionResponse); + // Request to generate the content using any defined generators + rpc GenerateContent(GenerateContentRequest) returns (GenerateContentResponse); + + // Start a mock server + rpc StartMockServer(StartMockServerRequest) returns (StartMockServerResponse); + // Shutdown a running mock server + // TODO: Replace the message types with MockServerRequest and MockServerResults in the next major version + rpc ShutdownMockServer(ShutdownMockServerRequest) returns (ShutdownMockServerResponse); + // Get the matching results from a running mock server + rpc GetMockServerResults(MockServerRequest) returns (MockServerResults); + + // Prepare an interaction for verification. This should return any data required to construct any request + // so that it can be amended before the verification is run + rpc PrepareInteractionForVerification(VerificationPreparationRequest) returns (VerificationPreparationResponse); + // Execute the verification for the interaction. + rpc VerifyInteraction(VerifyInteractionRequest) returns (VerifyInteractionResponse); +} \ No newline at end of file diff --git a/test/matt.consumer.integration.spec.ts b/test/matt.consumer.integration.spec.ts index 512bb2f9..fa439829 100644 --- a/test/matt.consumer.integration.spec.ts +++ b/test/matt.consumer.integration.spec.ts @@ -42,7 +42,8 @@ const sendMattMessageTCP = ( }); }; -describe.skip('MATT protocol test', () => { +const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; +(skipPluginTests ? describe.skip : describe)('MATT protocol test', () => { setLogLevel('trace'); let provider: ConsumerPact; @@ -60,7 +61,7 @@ describe.skip('MATT protocol test', () => { 'matt-provider', FfiSpecificationVersion['SPECIFICATION_VERSION_V4'] ); - provider.addPlugin('matt', '0.0.2'); + provider.addPlugin('matt', '0.1.1'); const interaction = provider.newInteraction(''); interaction.uponReceiving('A request to communicate via MATT'); @@ -131,7 +132,7 @@ describe.skip('MATT protocol test', () => { beforeEach(() => { const mattMessage = `{"request": {"body": "hellotcp"}, "response":{"body":"tcpworld"}}`; - tcpProvider.addPlugin('matt', '0.0.2'); + tcpProvider.addPlugin('matt', '0.1.1'); const message = tcpProvider.newSynchronousMessage('a MATT message'); message.withPluginRequestResponseInteractionContents( @@ -148,7 +149,7 @@ describe.skip('MATT protocol test', () => { }); it('generates a pact with success', async () => { - const message = await sendMattMessageTCP('hello', HOST, port); + const message = await sendMattMessageTCP('hellotcp', HOST, port); expect(message).to.eq('tcpworld'); const res = tcpProvider.mockServerMatchedSuccessfully(port); diff --git a/test/matt.provider.integration.spec.ts b/test/matt.provider.integration.spec.ts index adca3474..ad0097ed 100644 --- a/test/matt.provider.integration.spec.ts +++ b/test/matt.provider.integration.spec.ts @@ -52,7 +52,8 @@ const startTCPServer = (host: string, port: number) => { }); }; -describe.skip('MATT protocol test', () => { +const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; +(skipPluginTests ? describe.skip : describe)('MATT protocol test', () => { setLogLevel('info'); describe('HTTP and TCP Provider', () => { diff --git a/test/plugin-verifier.integration.spec.ts b/test/plugin-verifier.integration.spec.ts index 47c46de4..40f2d671 100644 --- a/test/plugin-verifier.integration.spec.ts +++ b/test/plugin-verifier.integration.spec.ts @@ -99,37 +99,41 @@ const getFeature = async (address: string, protoFile: string) => { }); }; -describe.skip('Plugin Verifier Integration Spec', () => { - context('plugin tests', () => { - describe('grpc interaction', () => { - before(async () => { - const server = getGRPCServer(); - startGRPCServer(server, GRPC_PORT); - await startHTTPServer(HTTP_PORT); - }); - - it('should verify the gRPC interactions', async () => { - await verifierFactory({ - providerBaseUrl: `http://127.0.0.1:${HTTP_PORT}`, - transports: [ - { - port: GRPC_PORT, - protocol: 'grpc', - }, - ], - logLevel: 'debug', - pactUrls: [`${__dirname}/integration/grpc/grpc.json`], - }).verify(); - - expect('').to.eq(''); - }); - - it('runs the grpc client', async () => { - const protoFile = `${__dirname}/integration/grpc/route_guide.proto`; - const feature = await getFeature(`127.0.0.1:${GRPC_PORT}`, protoFile); - - console.log(feature); +const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; +(skipPluginTests ? describe.skip : describe)( + 'Plugin Verifier Integration Spec', + () => { + context('plugin tests', () => { + describe('grpc interaction', () => { + before(async () => { + const server = getGRPCServer(); + startGRPCServer(server, GRPC_PORT); + await startHTTPServer(HTTP_PORT); + }); + + it('should verify the gRPC interactions', async () => { + await verifierFactory({ + providerBaseUrl: `http://127.0.0.1:${HTTP_PORT}`, + transports: [ + { + port: GRPC_PORT, + protocol: 'grpc', + }, + ], + logLevel: 'debug', + pactUrls: [`${__dirname}/integration/grpc/grpc.json`], + }).verify(); + + expect('').to.eq(''); + }); + + it('runs the grpc client', async () => { + const protoFile = `${__dirname}/integration/grpc/route_guide.proto`; + const feature = await getFeature(`127.0.0.1:${GRPC_PORT}`, protoFile); + + console.log(feature); + }); }); }); - }); -}); + } +); diff --git a/test/verifier.integration.spec.ts b/test/verifier.integration.spec.ts index 3d7ce917..94eda3df 100644 --- a/test/verifier.integration.spec.ts +++ b/test/verifier.integration.spec.ts @@ -175,7 +175,8 @@ describe('Verifier Integration Spec', () => { // thread '' panicked at 'Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context // with RUST_BACKTRACE=1 it seems that it relates to fetching from the broker, and something bad // is happening in reqwest - context.skip('from a Pact Broker', () => { + const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; + (skipPluginTests ? context.skip : context)('from a Pact Broker', () => { context('without authentication', () => { it('should return a successful promise', () => expect( From 880ac148d3295781f1c9deb716da3df12f2238ce Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:05:13 +0100 Subject: [PATCH 06/18] test: install shared-mime-info on linux to remove content-type condition --- test/consumer.integration.spec.ts | 159 +++++++++++++++--------------- test/message.integration.spec.ts | 80 ++++++++------- 2 files changed, 119 insertions(+), 120 deletions(-) diff --git a/test/consumer.integration.spec.ts b/test/consumer.integration.spec.ts index 059d33f0..0bd60e8a 100644 --- a/test/consumer.integration.spec.ts +++ b/test/consumer.integration.spec.ts @@ -21,17 +21,8 @@ const { expect } = chai; const HOST = '127.0.0.1'; const isWin = process.platform === 'win32'; -const isLinux = process.platform === 'linux'; const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; -const isDarwinX64 = process.platform === 'darwin' && process.arch === 'x64'; -const isLinuxArm64 = process.platform === 'linux' && process.arch === 'arm64'; -const isCirrusCi = process.env['CIRRUS_CI'] === 'true'; -const usesOctetStream = - isLinuxArm64 || - isWin || - isDarwinArm64 || - (isCirrusCi && isLinux) || - (isCirrusCi && isDarwinX64); +const usesOctetStream = isWin || isDarwinArm64; describe('FFI integration test for the HTTP Consumer API', () => { setLogLevel('trace'); @@ -264,78 +255,82 @@ describe('FFI integration test for the HTTP Consumer API', () => { }); // Should only run this if the plugin is installed - describe.skip('using a plugin (protobufs)', () => { - const protoFile = `${__dirname}/integration/plugin.proto`; - - beforeEach(() => { - pact = makeConsumerPact( - 'foo-consumer', - 'bar-provider', - FfiSpecificationVersion['SPECIFICATION_VERSION_V3'] - ); - pact.addPlugin('protobuf', '0.1.14'); - - const interaction = pact.newInteraction('some description'); - const protobufContents = { - 'pact:proto': protoFile, - 'pact:message-type': 'InitPluginRequest', - 'pact:content-type': 'application/protobuf', - implementation: "notEmpty('pact-js-driver')", - version: "matching(semver, '0.0.0')", - }; - - interaction.uponReceiving('a request to get a protobuf'); - interaction.given('protobuf state'); - interaction.withRequest('GET', '/protobuf'); - interaction.withPluginResponseInteractionContents( - 'application/protobuf', - JSON.stringify(protobufContents) - ); - interaction.withStatus(200); - - port = pact.createMockServer(HOST); - }); - - afterEach(() => { - pact.cleanupPlugins(); - }); - - it('generates a pact with success', async () => { - const root = await load(protoFile); - - // Obtain a message type - const InitPluginRequest = root.lookupType( - 'io.pact.plugin.InitPluginRequest' - ); - - return axios - .request({ - baseURL: `http://${HOST}:${port}`, - method: 'GET', - url: '/protobuf', - responseType: 'arraybuffer', - }) - .then((res) => { - const message: any = InitPluginRequest.decode(res.data); - expect(message.implementation).to.equal('pact-js-driver'); - expect(message.version).to.equal('0.0.0'); - }) - .then(() => { - expect(pact.mockServerMatchedSuccessfully(port)).to.be.true; - }) - .then(() => { - // You don't have to call this, it's just here to check it works - const mismatches = pact.mockServerMismatches(port); - expect(mismatches).to.have.length(0); - }) - .then(() => { - pact.writePactFile(path.join(__dirname, '__testoutput__')); - }) - .then(() => { - pact.cleanupMockServer(port); - }); - }); - }); + const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; + (skipPluginTests ? describe.skip : describe)( + 'using a plugin (protobufs)', + () => { + const protoFile = `${__dirname}/integration/plugin.proto`; + + beforeEach(() => { + pact = makeConsumerPact( + 'foo-consumer', + 'bar-provider', + FfiSpecificationVersion['SPECIFICATION_VERSION_V3'] + ); + pact.addPlugin('protobuf', '0.3.15'); + + const interaction = pact.newInteraction('some description'); + const protobufContents = { + 'pact:proto': protoFile, + 'pact:message-type': 'InitPluginRequest', + 'pact:content-type': 'application/protobuf', + implementation: "notEmpty('pact-js-driver')", + version: "matching(semver, '0.0.0')", + }; + + interaction.uponReceiving('a request to get a protobuf'); + interaction.given('protobuf state'); + interaction.withRequest('GET', '/protobuf'); + interaction.withPluginResponseInteractionContents( + 'application/protobuf', + JSON.stringify(protobufContents) + ); + interaction.withStatus(200); + + port = pact.createMockServer(HOST); + }); + + afterEach(() => { + pact.cleanupPlugins(); + }); + + it('generates a pact with success', async () => { + const root = await load(protoFile); + + // Obtain a message type + const InitPluginRequest = root.lookupType( + 'io.pact.plugin.InitPluginRequest' + ); + + return axios + .request({ + baseURL: `http://${HOST}:${port}`, + method: 'GET', + url: '/protobuf', + responseType: 'arraybuffer', + }) + .then((res) => { + const message: any = InitPluginRequest.decode(res.data); + expect(message.implementation).to.equal('pact-js-driver'); + expect(message.version).to.equal('0.0.0'); + }) + .then(() => { + expect(pact.mockServerMatchedSuccessfully(port)).to.be.true; + }) + .then(() => { + // You don't have to call this, it's just here to check it works + const mismatches = pact.mockServerMismatches(port); + expect(mismatches).to.have.length(0); + }) + .then(() => { + pact.writePactFile(path.join(__dirname, '__testoutput__')); + }) + .then(() => { + pact.cleanupMockServer(port); + }); + }); + } + ); describe('with multipart data', () => { const form = new FormData(); diff --git a/test/message.integration.spec.ts b/test/message.integration.spec.ts index 484d3607..a86b242c 100644 --- a/test/message.integration.spec.ts +++ b/test/message.integration.spec.ts @@ -14,12 +14,8 @@ chai.use(chaiAsPromised); const { expect } = chai; const isWin = process.platform === 'win32'; -const isLinux = process.platform === 'linux'; const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; -const isLinuxArm64 = process.platform === 'linux' && process.arch === 'arm64'; -const isCirrusCi = process.env['CIRRUS_CI'] === 'true'; -const usesOctetStream = - isLinuxArm64 || isWin || isDarwinArm64 || (isCirrusCi && isLinux); +const usesOctetStream = isWin || isDarwinArm64; const getFeature = async (address: string, protoFile: string) => { const def = await load(protoFile); @@ -146,17 +142,24 @@ describe('FFI integration test for the Message Consumer API', () => { }); }); - describe.skip('with plugin contents (gRPC)', () => { - const protoFile = `${__dirname}/integration/grpc/route_guide.proto`; + const skipPluginTests = process.env['SKIP_PLUGIN_TESTS'] === 'true'; + (skipPluginTests ? describe.skip : describe)( + 'with plugin contents (gRPC)', + () => { + let protoFile = `${__dirname}/integration/grpc/route_guide.proto`; + if (process.platform === 'win32') { + const escapedProtoFile = protoFile.replace(/\\/g, '\\\\'); + protoFile = escapedProtoFile; + } - let port: number; + let port: number; - afterEach(() => { - pact.cleanupPlugins(); - }); + afterEach(() => { + pact.cleanupPlugins(); + }); - beforeEach(() => { - const grpcInteraction = `{ + beforeEach(() => { + const grpcInteraction = `{ "pact:proto": "${protoFile}", "pact:proto-service": "RouteGuide/GetFeature", "pact:content-type": "application/protobuf", @@ -173,36 +176,37 @@ describe('FFI integration test for the Message Consumer API', () => { } }`; - pact.addMetadata('pact-node', 'meta-key', 'meta-val'); - pact.addPlugin('protobuf', '0.1.14'); + pact.addMetadata('pact-node', 'meta-key', 'meta-val'); + pact.addPlugin('protobuf', '0.3.15'); - const message = pact.newSynchronousMessage('a grpc test 1'); - message.given('some state 1'); - message.withPluginRequestResponseInteractionContents( - 'application/protobuf', - grpcInteraction - ); - message.withMetadata('meta-key 1', 'meta-val 2'); + const message = pact.newSynchronousMessage('a grpc test 1'); + message.given('some state 1'); + message.withPluginRequestResponseInteractionContents( + 'application/protobuf', + grpcInteraction + ); + message.withMetadata('meta-key 1', 'meta-val 2'); - port = pact.pactffiCreateMockServerForTransport( - '127.0.0.1', - 'grpc', - '' - ); - }); + port = pact.pactffiCreateMockServerForTransport( + '127.0.0.1', + 'grpc', + '' + ); + }); - it('generates a pact with success', async () => { - const feature: any = await getFeature(`127.0.0.1:${port}`, protoFile); - expect(feature.name).to.eq('Big Tree'); + it('generates a pact with success', async () => { + const feature: any = await getFeature(`127.0.0.1:${port}`, protoFile); + expect(feature.name).to.eq('Big Tree'); - const res = pact.mockServerMatchedSuccessfully(port); - expect(res).to.eq(true); + const res = pact.mockServerMatchedSuccessfully(port); + expect(res).to.eq(true); - const mismatches = pact.mockServerMismatches(port); - expect(mismatches.length).to.eq(0); + const mismatches = pact.mockServerMismatches(port); + expect(mismatches.length).to.eq(0); - pact.writePactFile(path.join(__dirname, '__testoutput__')); - }); - }); + pact.writePactFile(path.join(__dirname, '__testoutput__')); + }); + } + ); }); }); From 9cdf34a156e3f5c063ca5559367ce9277356a13f Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:05:55 +0100 Subject: [PATCH 07/18] fix: can deploy error, as json output could not be parsed --- src/can-deploy/can-deploy.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/can-deploy/can-deploy.ts b/src/can-deploy/can-deploy.ts index 32643020..6364f323 100644 --- a/src/can-deploy/can-deploy.ts +++ b/src/can-deploy/can-deploy.ts @@ -100,7 +100,7 @@ export class CanDeploy { ], this.__argMapping ); - const output: Array = []; + let output: Array = []; if (instance.stdout && instance.stderr) { instance.stdout.on('data', (l) => output.push(l)); instance.stderr.on('data', (l) => output.push(l)); @@ -110,9 +110,15 @@ export class CanDeploy { if (this.options.output === 'json') { try { - const startIndex = output.findIndex((l: string | Buffer) => - l.toString().startsWith('{') - ); + const findJsonIndex = (data: (string | Buffer)[]) => + data.findIndex((l: string | Buffer) => + l.toString().startsWith('{') + ); + let startIndex = findJsonIndex(output); + if (startIndex === -1) { + output = output.toString().split('\n'); + startIndex = findJsonIndex(output); + } if (startIndex === -1) { logger.error( `can-i-deploy produced no json output:\n${result}` From 3446e480eedb3711a616d6d0acdd22a21d106060 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:06:48 +0100 Subject: [PATCH 08/18] ci(test): update to node 22 + test docker cross version --- .github/workflows/build-and-test.yml | 175 +++++++++++++++++++++------ .gitignore | 1 + 2 files changed, 142 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b6de64b9..988d3a00 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -7,7 +7,6 @@ on: workflow_dispatch: jobs: - create_pre_release: runs-on: ubuntu-latest steps: @@ -15,12 +14,12 @@ jobs: with: fetch-depth: 0 - run: GH_CREATE_PRE_RELEASE=true ./script/ci/release.sh - if: github.ref == 'refs/heads/master' && env.ACT != 'true' && runner.os == 'Linux' + if: github.ref == 'refs/heads/master' && env.ACT != true && runner.os == 'Linux' env: GITHUB_TOKEN: ${{ github.token }} - + prebuild: - needs: [ create_pre_release ] + needs: [create_pre_release] runs-on: ${{ matrix.os }} defaults: run: @@ -29,7 +28,32 @@ jobs: fail-fast: false matrix: node-version: [20] - os: [macos-14,macos-12,ubuntu-latest,windows-latest] + os: [ + macos-12, + ubuntu-latest, + windows-latest, + ] + docker: [false] + alpine: [false] + arch: ['amd64'] + include: + - os: ubuntu-latest + docker: true + alpine: false + arch: arm64 + - os: ubuntu-latest + docker: true + alpine: true + arch: arm64 + - os: ubuntu-latest + docker: true + alpine: true + arch: amd64 + - os: macos-14 + docker: false + alpine: false + arch: arm64 + name: Prebuild ${{ matrix.docker == true && matrix.alpine == true && 'linux-musl' || matrix.docker == true && matrix.alpine == false && 'linux' || matrix.os }}-${{ matrix.arch }} env: NODE_VERSION: ${{ matrix.node-version }} @@ -38,7 +62,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - + - name: Use Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v4 with: @@ -47,27 +71,32 @@ jobs: - if: runner.os == 'Windows' run: echo "ONLY_DOWNLOAD_PACT_FOR_WINDOWS=true" >> $GITHUB_ENV - - run: ./script/ci/prebuild.sh - - name: Set up QEMU - if: runner.os == 'Linux' + if: ${{ matrix.docker == true && matrix.arch == 'arm64' }} uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - if: runner.os == 'Linux' - uses: docker/setup-buildx-action@v3 - - if: runner.os == 'Linux' - name: prebuild arm64 - run: docker run -v $PWD:/home --platform linux/arm64 --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh' + - if: ${{ matrix.docker == true && matrix.alpine == true }} + name: prebuild linux ${{ matrix.arch }} musl + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home && bash -c "/home/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' + + - if: ${{ matrix.docker == true && matrix.alpine != true }} + name: prebuild linux ${{ matrix.arch }} + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh && rm -rf ffi node_modules' + + - run: sudo chown -R $(id -u):$(id -g) prebuilds + if: ${{ matrix.docker == true }} + + - run: ./script/ci/prebuild.sh + if: ${{ matrix.docker != true }} - name: Upload prebuild for ${{ runner.os }}-${{ runner.arch }} uses: actions/upload-artifact@v3 with: path: prebuilds/*.tar.gz - + - run: GH_PRE_RELEASE_UPLOAD=true ./script/ci/release.sh - if: github.ref == 'refs/heads/master' && env.ACT != 'true' + if: github.ref == 'refs/heads/master' && env.ACT != true env: - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ github.token }} test: runs-on: ${{ matrix.os }} @@ -78,8 +107,72 @@ jobs: strategy: fail-fast: false matrix: - node-version: [16,18,20] - os: [macos-14, macos-12, ubuntu-latest,windows-latest] + node-version: [16, 18, 20, 22] + os: [macos-14, macos-12, ubuntu-latest, windows-latest] + docker: [false] + include: + - os: ubuntu-latest + docker: true + alpine: false + arch: arm64 + node-version: 22 + - os: ubuntu-latest + docker: true + alpine: true + arch: arm64 + node-version: 22 + - os: ubuntu-latest + docker: true + alpine: true + arch: amd64 + node-version: 22 + - os: ubuntu-latest + docker: true + alpine: false + arch: arm64 + node-version: 20 + - os: ubuntu-latest + docker: true + alpine: true + arch: arm64 + node-version: 20 + - os: ubuntu-latest + docker: true + alpine: true + arch: amd64 + node-version: 20 + - os: ubuntu-latest + docker: true + alpine: false + arch: arm64 + node-version: 18 + - os: ubuntu-latest + docker: true + alpine: true + arch: arm64 + node-version: 18 + - os: ubuntu-latest + docker: true + alpine: true + arch: amd64 + node-version: 18 + - os: ubuntu-latest + docker: true + alpine: false + arch: arm64 + node-version: 16 + - os: ubuntu-latest + docker: true + alpine: true + arch: arm64 + node-version: 16 + - os: ubuntu-latest + docker: true + alpine: true + arch: amd64 + node-version: 16 + + name: Test ${{ matrix.docker == true && matrix.alpine == true && 'linux-musl' || matrix.docker == true && matrix.alpine == false && 'linux' || matrix.os }}-${{ matrix.arch }}-node-${{ matrix.node-version }} env: NODE_VERSION: ${{ matrix.node-version }} @@ -100,24 +193,38 @@ jobs: - if: runner.os == 'Windows' run: echo "ONLY_DOWNLOAD_PACT_FOR_WINDOWS=true" >> $GITHUB_ENV - - run: LOG_LEVEL=debug ./script/ci/unpack-and-test.sh + - if: matrix.os == 'macos-14' + run: brew install protobuf + - run: LOG_LEVEL=debug ./script/ci/unpack-and-test.sh + if: ${{ matrix.docker != true }} # Linux aarch64 tests are skipped as QEMU fails when executing the ruby binaries - # Related comment: https://github.com/phusion/passenger/issues/2288#issuecomment-1387625121 - # - name: Set up QEMU - # if: runner.os == 'Linux' - # uses: docker/setup-qemu-action@v3 - # - name: Set up Docker Buildx - # if: runner.os == 'Linux' - # uses: docker/setup-buildx-action@v3 - # - if: runner.os == 'Linux' - # name: test arm64 - # run: docker run -v $PWD:/home --platform linux/arm64 --rm node:20 bin/bash -c 'cd /home && /home/script/ci/unpack-and-test.sh' + # Related comment: https://github.com/phusion/passenger/issues/2288#issuecomment-1387625121 + + - name: Set up QEMU + if: ${{ matrix.docker == true && matrix.arch == 'arm64' }} + uses: docker/setup-qemu-action@v3 + + - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' }} + name: test arm64 + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home && /home/script/ci/unpack-and-test.sh' + env: + SKIP_STANDALONE_TESTS: true + + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' }} + name: test linux amd64 musl + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home && /home/script/ci/unpack-and-test.sh' + + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' }} + name: test linux arm64 musl + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home && /home/script/ci/unpack-and-test.sh' + env: + SKIP_STANDALONE_TESTS: true release_dry_run: runs-on: ubuntu-latest - needs: [ create_pre_release, prebuild ] + needs: [create_pre_release, prebuild] if: github.ref == 'refs/heads/master' env: @@ -134,11 +241,11 @@ jobs: node-version: ${{ env.NODE_VERSION }} registry-url: 'https://registry.npmjs.org' - - name: "release - dry run: ${{ env.DRY_RUN }}" + - name: 'release - dry run: ${{ env.DRY_RUN }}' id: publish run: script/ci/release.sh env: GITHUB_TOKEN: ${{ github.token }} DRY_RUN: true - - run: echo "New Release will be v${{ steps.publish.outputs.version }}" \ No newline at end of file + - run: echo "New Release will be v${{ steps.publish.outputs.version }}" diff --git a/.gitignore b/.gitignore index a3d54657..aa8b1bae 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ standalone/*.gz standalone/README.md # FFI native bindings *.so +*.a *.dll* *.dylib pact.h From d44430617a7796f1842c18bb4aede1f118364008 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:37:29 +0100 Subject: [PATCH 09/18] ci: fixes for node 16 and 18 --- script/ci/build-and-test.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/script/ci/build-and-test.sh b/script/ci/build-and-test.sh index abc3c01b..aa8d52d5 100755 --- a/script/ci/build-and-test.sh +++ b/script/ci/build-and-test.sh @@ -19,7 +19,16 @@ fi node --version npm --version - +if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *18* ]]; then +# npm ERR! code ECONNRESET +# npm ERR! errno ECONNRESET +# npm ERR! network request to https://registry.npmjs.org/ failed, reason: Client network socket disconnected before secure TLS connection was established + npm config set maxsockets 1 +fi +if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *16* ]]; then + # Setting the cache location is a workaround for node 16 install errors https://github.com/npm/cli/issues/5114 + npm config set cache /tmp +fi npm ci --ignore-scripts npm run format:check From adbc60eba8874aa40311ecacb49accd5749ad422 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 22:56:50 +0100 Subject: [PATCH 10/18] ci: /home/node/app / maxsockets 1 / npm cache /tmp --- .github/workflows/build-and-test.yml | 31 +++++++++++++++++++++------- script/ci/build-and-test.sh | 22 ++++++++++---------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 988d3a00..5bfb964c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,11 +76,11 @@ jobs: uses: docker/setup-qemu-action@v3 - if: ${{ matrix.docker == true && matrix.alpine == true }} name: prebuild linux ${{ matrix.arch }} musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home && bash -c "/home/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home/node/app && bash -c "/home/node/app/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' - if: ${{ matrix.docker == true && matrix.alpine != true }} name: prebuild linux ${{ matrix.arch }} - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh && rm -rf ffi node_modules' + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home/node/app && /home/node/app/script/ci/prebuild.sh && rm -rf ffi node_modules' - run: sudo chown -R $(id -u):$(id -g) prebuilds if: ${{ matrix.docker == true }} @@ -206,19 +206,34 @@ jobs: if: ${{ matrix.docker == true && matrix.arch == 'arm64' }} uses: docker/setup-qemu-action@v3 - - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' }} + - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' && matrix.node-version != 16 }} name: test arm64 - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' }} + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' && matrix.node-version != 16 }} name: test linux amd64 musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' }} + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' && matrix.node-version != 16 }} name: test linux arm64 musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + env: + SKIP_STANDALONE_TESTS: true + - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' && matrix.node-version == 16 }} + name: test arm64 + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'npm config set cache /tmp && apt install -y shared-mime-info && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + env: + SKIP_STANDALONE_TESTS: true + + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' && matrix.node-version == 16 }} + name: test linux amd64 musl + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'npm config set cache /tmp && apk add bash curl gcompat shared-mime-info file && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' && matrix.node-version == 16 }} + name: test linux arm64 musl + run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'npm config set cache /tmp && apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true diff --git a/script/ci/build-and-test.sh b/script/ci/build-and-test.sh index aa8d52d5..53b70fa3 100755 --- a/script/ci/build-and-test.sh +++ b/script/ci/build-and-test.sh @@ -19,17 +19,17 @@ fi node --version npm --version -if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *18* ]]; then -# npm ERR! code ECONNRESET -# npm ERR! errno ECONNRESET -# npm ERR! network request to https://registry.npmjs.org/ failed, reason: Client network socket disconnected before secure TLS connection was established - npm config set maxsockets 1 -fi -if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *16* ]]; then - # Setting the cache location is a workaround for node 16 install errors https://github.com/npm/cli/issues/5114 - npm config set cache /tmp -fi -npm ci --ignore-scripts +# if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *18* ]]; then +# # npm ERR! code ECONNRESET +# # npm ERR! errno ECONNRESET +# # npm ERR! network request to https://registry.npmjs.org/ failed, reason: Client network socket disconnected before secure TLS connection was established +# npm config set maxsockets 1 +# fi +# if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *16* ]]; then +# # Setting the cache location is a workaround for node 16 install errors https://github.com/npm/cli/issues/5114 +# npm config set cache /tmp +# fi +npm ci --ignore-scripts --maxsockets 1 npm run format:check npm run lint From a7883aa60b86a626f0638802ac04e4689940c759 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 11 May 2024 23:19:43 +0100 Subject: [PATCH 11/18] ci: drop testing node 16 / node 18 arm64 linux in gha --- .github/workflows/build-and-test.yml | 71 +++++++++++----------------- script/ci/build-and-test.sh | 12 +---- 2 files changed, 29 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5bfb964c..e93a5159 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,11 +76,11 @@ jobs: uses: docker/setup-qemu-action@v3 - if: ${{ matrix.docker == true && matrix.alpine == true }} name: prebuild linux ${{ matrix.arch }} musl - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home/node/app && bash -c "/home/node/app/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home && bash -c "/home/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' - if: ${{ matrix.docker == true && matrix.alpine != true }} name: prebuild linux ${{ matrix.arch }} - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home/node/app && /home/node/app/script/ci/prebuild.sh && rm -rf ffi node_modules' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh && rm -rf ffi node_modules' - run: sudo chown -R $(id -u):$(id -g) prebuilds if: ${{ matrix.docker == true }} @@ -141,11 +141,6 @@ jobs: alpine: true arch: amd64 node-version: 20 - - os: ubuntu-latest - docker: true - alpine: false - arch: arm64 - node-version: 18 - os: ubuntu-latest docker: true alpine: true @@ -156,21 +151,26 @@ jobs: alpine: true arch: amd64 node-version: 18 - - os: ubuntu-latest - docker: true - alpine: false - arch: arm64 - node-version: 16 - - os: ubuntu-latest - docker: true - alpine: true - arch: arm64 - node-version: 16 - - os: ubuntu-latest - docker: true - alpine: true - arch: amd64 - node-version: 16 + # - os: ubuntu-latest + # docker: true + # alpine: false + # arch: arm64 + # node-version: 18 + # - os: ubuntu-latest + # docker: true + # alpine: false + # arch: arm64 + # node-version: 16 + # - os: ubuntu-latest + # docker: true + # alpine: true + # arch: arm64 + # node-version: 16 + # - os: ubuntu-latest + # docker: true + # alpine: true + # arch: amd64 + # node-version: 16 name: Test ${{ matrix.docker == true && matrix.alpine == true && 'linux-musl' || matrix.docker == true && matrix.alpine == false && 'linux' || matrix.os }}-${{ matrix.arch }}-node-${{ matrix.node-version }} @@ -206,34 +206,19 @@ jobs: if: ${{ matrix.docker == true && matrix.arch == 'arm64' }} uses: docker/setup-qemu-action@v3 - - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' && matrix.node-version != 16 }} - name: test arm64 - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' - env: - SKIP_STANDALONE_TESTS: true - - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' && matrix.node-version != 16 }} - name: test linux amd64 musl - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' - - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' && matrix.node-version != 16 }} - name: test linux arm64 musl - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' - env: - SKIP_STANDALONE_TESTS: true - - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' && matrix.node-version == 16 }} + - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' }} name: test arm64 - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'npm config set cache /tmp && apt install -y shared-mime-info && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home && /home/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' && matrix.node-version == 16 }} + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' }} name: test linux amd64 musl - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'npm config set cache /tmp && apk add bash curl gcompat shared-mime-info file && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home && /home/script/ci/unpack-and-test.sh' - - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' && matrix.node-version == 16 }} + - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' }} name: test linux arm64 musl - run: docker run -v $PWD:/home/node/app --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'npm config set cache /tmp && apk add bash curl libc6-compat gcompat shared-mime-info file protoc protobuf-dev && cd /home/node/app && /home/node/app/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl shared-mime-info file protoc protobuf-dev && cd /home && /home/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true diff --git a/script/ci/build-and-test.sh b/script/ci/build-and-test.sh index 53b70fa3..2908487d 100755 --- a/script/ci/build-and-test.sh +++ b/script/ci/build-and-test.sh @@ -19,17 +19,7 @@ fi node --version npm --version -# if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *18* ]]; then -# # npm ERR! code ECONNRESET -# # npm ERR! errno ECONNRESET -# # npm ERR! network request to https://registry.npmjs.org/ failed, reason: Client network socket disconnected before secure TLS connection was established -# npm config set maxsockets 1 -# fi -# if [[ ${CI:-} == 'true' && "$(uname -s)" == 'Linux' && "$(uname -m)" == 'aarch64' && "$(node --version)" == *16* ]]; then -# # Setting the cache location is a workaround for node 16 install errors https://github.com/npm/cli/issues/5114 -# npm config set cache /tmp -# fi -npm ci --ignore-scripts --maxsockets 1 +npm ci --ignore-scripts npm run format:check npm run lint From 9d00ec6a4a6f94ed61a84c4e185abee3b2aed33a Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Mon, 27 May 2024 13:37:32 +0100 Subject: [PATCH 12/18] fixes: content_type_detection_plus_aarch64_musl_so --- script/lib/download-ffi.sh | 2 +- src/ffi/index.ts | 2 +- test/consumer.integration.spec.ts | 13 +++++++------ test/message.integration.spec.ts | 9 +++++---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/script/lib/download-ffi.sh b/script/lib/download-ffi.sh index d66506a1..32af5dc6 100755 --- a/script/lib/download-ffi.sh +++ b/script/lib/download-ffi.sh @@ -8,7 +8,7 @@ require_binary gunzip require_env_var FFI_VERSION -BASEURL=https://github.com/pact-foundation/pact-reference/releases/download +BASEURL=https://github.com/you54f/pact-reference/releases/download FFI_DIR="${LIB_DIR}/../../ffi" if [[ $(find "${FFI_DIR}" -name "${FFI_VERSION}*") ]]; then diff --git a/src/ffi/index.ts b/src/ffi/index.ts index 74a9f67e..14b7bd98 100644 --- a/src/ffi/index.ts +++ b/src/ffi/index.ts @@ -4,7 +4,7 @@ import logger, { DEFAULT_LOG_LEVEL } from '../logger'; import { LogLevel } from '../logger/types'; import { Ffi } from './types'; -export const PACT_FFI_VERSION = '0.4.20'; +export const PACT_FFI_VERSION = '0.4.22'; // supported prebuilds // darwin-arm64 diff --git a/test/consumer.integration.spec.ts b/test/consumer.integration.spec.ts index 0bd60e8a..8fc0cf04 100644 --- a/test/consumer.integration.spec.ts +++ b/test/consumer.integration.spec.ts @@ -20,9 +20,9 @@ const { expect } = chai; const HOST = '127.0.0.1'; -const isWin = process.platform === 'win32'; -const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; -const usesOctetStream = isWin || isDarwinArm64; +// const isWin = process.platform === 'win32'; +// const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; +// const usesOctetStream = isWin || isDarwinArm64; describe('FFI integration test for the HTTP Consumer API', () => { setLogLevel('trace'); @@ -75,9 +75,10 @@ describe('FFI integration test for the HTTP Consumer API', () => { .request({ baseURL: `http://${HOST}:${port}`, headers: { - 'content-type': usesOctetStream - ? 'application/octet-stream' - : 'application/gzip', + // 'content-type': usesOctetStream + // ? 'application/octet-stream' + // : 'application/gzip', + 'content-type': 'application/gzip', Accept: 'application/json', 'x-special-header': 'header', }, diff --git a/test/message.integration.spec.ts b/test/message.integration.spec.ts index a86b242c..15150916 100644 --- a/test/message.integration.spec.ts +++ b/test/message.integration.spec.ts @@ -13,9 +13,9 @@ import { setLogLevel } from '../src/logger'; chai.use(chaiAsPromised); const { expect } = chai; -const isWin = process.platform === 'win32'; -const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; -const usesOctetStream = isWin || isDarwinArm64; +// const isWin = process.platform === 'win32'; +// const isDarwinArm64 = process.platform === 'darwin' && process.arch === 'arm64'; +// const usesOctetStream = isWin || isDarwinArm64; const getFeature = async (address: string, protoFile: string) => { const def = await load(protoFile); @@ -100,7 +100,8 @@ describe('FFI integration test for the Message Consumer API', () => { message.givenWithParam('some state 2', 'state2 key', 'state2 val'); message.withBinaryContents( bytes, - usesOctetStream ? 'application/octet-stream' : 'application/gzip' + // usesOctetStream ? 'application/octet-stream' : 'application/gzip' + 'application/gzip' ); message.withMetadata('meta-key', 'meta-val'); From 02d17b8ff50aa189c7379ec56887bd8a2fc990ca Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Mon, 27 May 2024 14:00:10 +0100 Subject: [PATCH 13/18] chore: remove usesOctetStream workaround --- test/consumer.integration.spec.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/consumer.integration.spec.ts b/test/consumer.integration.spec.ts index 8fc0cf04..60b78ff9 100644 --- a/test/consumer.integration.spec.ts +++ b/test/consumer.integration.spec.ts @@ -196,7 +196,8 @@ describe('FFI integration test for the HTTP Consumer API', () => { interaction.withQuery('someParam', 0, 'someValue'); interaction.withRequestBinaryBody( bytes, - usesOctetStream ? 'application/octet-stream' : 'application/gzip' + 'application/gzip' + // usesOctetStream ? 'application/octet-stream' : 'application/gzip' ); interaction.withResponseBody( JSON.stringify({ @@ -219,9 +220,10 @@ describe('FFI integration test for the HTTP Consumer API', () => { .request({ baseURL: `http://${HOST}:${port}`, headers: { - 'content-type': usesOctetStream - ? 'application/octet-stream' - : 'application/gzip', + 'content-type': 'application/gzip', + // 'content-type': usesOctetStream + // ? 'application/octet-stream' + // : 'application/gzip', Accept: 'application/json', 'x-special-header': 'header', }, From 64e75d6d44954f559168849329a441f3f53ea5bc Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Mon, 27 May 2024 14:47:22 +0100 Subject: [PATCH 14/18] chore: remove strip from musl prebuild --- script/ci/prebuild-alpine.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/ci/prebuild-alpine.sh b/script/ci/prebuild-alpine.sh index 0d5bfc4f..26751209 100755 --- a/script/ci/prebuild-alpine.sh +++ b/script/ci/prebuild-alpine.sh @@ -1,4 +1,3 @@ - #!/bin/bash -eu SCRIPT_DIR="$(cd "$(dirname "${0}")"; pwd)" # Figure out where the script is running @@ -15,7 +14,7 @@ apk add bash curl python3 make g++ rm -rf build node_modules npm ci --ignore-scripts export npm_config_target=${NODE_VERSION} -npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --libc musl --tag-libc --strip --name ${PREBUILD_NAME} +npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --libc musl --tag-libc --name ${PREBUILD_NAME} ls prebuilds/**/* ARCH=$(uname -m | tr '[:upper:]' '[:lower:]') case $ARCH in From 79abe28b4e5c79cec21ae0c97b739c33c4589871 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Mon, 27 May 2024 15:34:51 +0100 Subject: [PATCH 15/18] fix(build): run ranlib on stripped libpact_ffi.a on musl targets --- script/ci/prebuild-alpine.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/ci/prebuild-alpine.sh b/script/ci/prebuild-alpine.sh index 26751209..d6427539 100755 --- a/script/ci/prebuild-alpine.sh +++ b/script/ci/prebuild-alpine.sh @@ -14,7 +14,8 @@ apk add bash curl python3 make g++ rm -rf build node_modules npm ci --ignore-scripts export npm_config_target=${NODE_VERSION} -npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --libc musl --tag-libc --name ${PREBUILD_NAME} +ranlib ffi/*/libpact_ffi.a +npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --libc musl --strip --tag-libc --name ${PREBUILD_NAME} ls prebuilds/**/* ARCH=$(uname -m | tr '[:upper:]' '[:lower:]') case $ARCH in From 752809c5b45d8bb9ebdd27f555566e06256a71be Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Tue, 28 May 2024 01:06:08 +0100 Subject: [PATCH 16/18] fix: switch to musl .so rather .a ranlib linker error in 0.4.21/0.4.22 fork --- .github/workflows/build-and-test.yml | 10 +++++----- binding.gyp | 16 ++++++++++++++-- script/lib/download-ffi.sh | 6 ++++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e93a5159..4ccbf77d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,11 +76,11 @@ jobs: uses: docker/setup-qemu-action@v3 - if: ${{ matrix.docker == true && matrix.alpine == true }} name: prebuild linux ${{ matrix.arch }} musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20-alpine bin/sh -c 'apk add bash && cd /home && bash -c "/home/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:20-alpine bin/sh -c 'apk add bash && cd /home && bash -c "/home/script/ci/prebuild-alpine.sh" && rm -rf ffi node_modules' - if: ${{ matrix.docker == true && matrix.alpine != true }} name: prebuild linux ${{ matrix.arch }} - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e CIRRUS_CI=true --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh && rm -rf ffi node_modules' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:20 bin/bash -c 'cd /home && /home/script/ci/prebuild.sh && rm -rf ffi node_modules' - run: sudo chown -R $(id -u):$(id -g) prebuilds if: ${{ matrix.docker == true }} @@ -208,17 +208,17 @@ jobs: - if: ${{ matrix.docker == true && matrix.alpine != true && matrix.arch == 'arm64' }} name: test arm64 - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'apt install -y shared-mime-info && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }} bin/bash -c 'cd /home && /home/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'amd64' }} name: test linux amd64 musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat shared-mime-info file && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl gcompat file && cd /home && /home/script/ci/unpack-and-test.sh' - if: ${{ matrix.docker == true && matrix.alpine == true && matrix.arch == 'arm64' }} name: test linux arm64 musl - run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl shared-mime-info file protoc protobuf-dev && cd /home && /home/script/ci/unpack-and-test.sh' + run: docker run -v $PWD:/home --platform linux/${{ matrix.arch }} -e SKIP_STANDALONE_TESTS --rm node:${{ matrix.node-version }}-alpine bin/sh -c 'apk add bash curl file protoc protobuf-dev && cd /home && /home/script/ci/unpack-and-test.sh' env: SKIP_STANDALONE_TESTS: true diff --git a/binding.gyp b/binding.gyp index e041dde8..033e05e1 100644 --- a/binding.gyp +++ b/binding.gyp @@ -89,9 +89,15 @@ { "link_settings": { "libraries": [ - "<(module_root_dir)/ffi/linux-x86_64/libpact_ffi.a" + "-lpact_ffi_musl", + "-L<(module_root_dir)/ffi/linux-musl-x86_64/", + "-Wl,-rpath,'$$ORIGIN'" ] }, + "copies": [{ + "files": ["<(module_root_dir)/ffi/linux-musl-x86_64/libpact_ffi_musl.so"], + "destination": "<(PRODUCT_DIR)" + }], } ], [ @@ -99,9 +105,15 @@ { "link_settings": { "libraries": [ - "<(module_root_dir)/ffi/linux-aarch64/libpact_ffi.a" + "-lpact_ffi_musl", + "-L<(module_root_dir)/ffi/linux-musl-aarch64/", + "-Wl,-rpath,'$$ORIGIN'" ] }, + "copies": [{ + "files": ["<(module_root_dir)/ffi/linux-musl-aarch64/libpact_ffi_musl.so"], + "destination": "<(PRODUCT_DIR)" + }], } ], [ diff --git a/script/lib/download-ffi.sh b/script/lib/download-ffi.sh index 32af5dc6..1e882576 100755 --- a/script/lib/download-ffi.sh +++ b/script/lib/download-ffi.sh @@ -20,9 +20,11 @@ warn "Cleaning ffi directory $FFI_DIR" rm -rf "${FFI_DIR:?}" mkdir -p "$FFI_DIR/osx-x86_64" mkdir -p "$FFI_DIR/linux-x86_64" +mkdir -p "$FFI_DIR/linux-musl-x86_64" mkdir -p "$FFI_DIR/windows-x86_64" mkdir -p "$FFI_DIR/osx-aarch64" mkdir -p "$FFI_DIR/linux-aarch64" +mkdir -p "$FFI_DIR/linux-musl-aarch64" function download_ffi_file { if [ -z "${1:-}" ]; then @@ -65,8 +67,8 @@ fi if [ -z "${ONLY_DOWNLOAD_PACT_FOR_WINDOWS:-}" ]; then download_ffi "linux-x86_64.so.gz" "lib" "linux-x86_64/libpact_ffi.so.gz" download_ffi "linux-aarch64.so.gz" "lib" "linux-aarch64/libpact_ffi.so.gz" - download_ffi "linux-x86_64-musl.a.gz" "lib" "linux-x86_64/libpact_ffi.a.gz" - download_ffi "linux-aarch64-musl.a.gz" "lib" "linux-aarch64/libpact_ffi.a.gz" + download_ffi "linux-x86_64-musl.so.gz" "lib" "linux-musl-x86_64/libpact_ffi_musl.so.gz" + download_ffi "linux-aarch64-musl.so.gz" "lib" "linux-musl-aarch64/libpact_ffi_musl.so.gz" download_ffi "osx-x86_64.dylib.gz" "lib" "osx-x86_64/libpact_ffi.dylib.gz" download_ffi "osx-aarch64-apple-darwin.dylib.gz" "lib" "osx-aarch64/libpact_ffi.dylib.gz" else From 73204f36bc8b2f86a2dd59bba9afb753091593ec Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Tue, 28 May 2024 01:08:21 +0100 Subject: [PATCH 17/18] chore: skip ranlib as not ar file --- script/ci/prebuild-alpine.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/ci/prebuild-alpine.sh b/script/ci/prebuild-alpine.sh index d6427539..fb401070 100755 --- a/script/ci/prebuild-alpine.sh +++ b/script/ci/prebuild-alpine.sh @@ -14,7 +14,8 @@ apk add bash curl python3 make g++ rm -rf build node_modules npm ci --ignore-scripts export npm_config_target=${NODE_VERSION} -ranlib ffi/*/libpact_ffi.a +# ar -s ffi/*/libpact_ffi.a +# ranlib ffi/*/libpact_ffi.a npx --yes prebuildify@${PREBUILDIFY_VERSION} --napi --libc musl --strip --tag-libc --name ${PREBUILD_NAME} ls prebuilds/**/* ARCH=$(uname -m | tr '[:upper:]' '[:lower:]') From fbffd0b6406683ded71ece2a98418a159ff709e5 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Tue, 28 May 2024 01:18:10 +0100 Subject: [PATCH 18/18] chore: release lib check --- script/ci/check-release-libs.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/script/ci/check-release-libs.sh b/script/ci/check-release-libs.sh index 4c9512b2..e8b64007 100755 --- a/script/ci/check-release-libs.sh +++ b/script/ci/check-release-libs.sh @@ -92,14 +92,22 @@ ls -1 prebuilds/** [[ -f prebuilds/darwin-arm64/libpact_ffi.dylib ]] || ERRORS='prebuilds/darwin-arm64/libpact_ffi.dylib' [[ -f prebuilds/darwin-arm64/node.napi.node ]] || ERRORS='prebuilds/darwin-arm64/node.napi.node' + [[ -f prebuilds/darwin-x64/libpact_ffi.dylib ]] || ERRORS='prebuilds/darwin-x64/libpact_ffi.dylib' [[ -f prebuilds/darwin-x64/node.napi.node ]] || ERRORS='prebuilds/darwin-x64/node.napi.node' + [[ -f prebuilds/linux-arm64/libpact_ffi.so ]] || ERRORS='prebuilds/linux-arm64/libpact_ffi.so' [[ -f prebuilds/linux-arm64/node.napi.node ]] || ERRORS='prebuilds/linux-arm64/node.napi.node' -[[ -f prebuilds/linux-arm64/node.napi.node ]] || ERRORS='prebuilds/linux-arm64/node.napi.musl.node' + +[[ -f prebuilds/linux-arm64/libpact_ffi_musl.so ]] || ERRORS='prebuilds/linux-arm64/libpact_ffi_musl.so' +[[ -f prebuilds/linux-arm64/node.napi.musl.node ]] || ERRORS='prebuilds/linux-arm64/node.napi.musl.node' + [[ -f prebuilds/linux-x64/libpact_ffi.so ]] || ERRORS='prebuilds/linux-x64/libpact_ffi.so' [[ -f prebuilds/linux-x64/node.napi.node ]] || ERRORS='prebuilds/linux-x64/node.napi.node' + +[[ -f prebuilds/linux-x64/libpact_ffi_musl.so ]] || ERRORS='prebuilds/linux-x64/libpact_ffi_musl.so' [[ -f prebuilds/linux-x64/node.napi.musl.node ]] || ERRORS='prebuilds/linux-x64/node.napi.musl.node' + [[ -f prebuilds/win32-x64/pact_ffi.dll ]] || ERRORS='prebuilds/win32-x64/pact_ffi.dll' [[ -f prebuilds/win32-x64/node.napi.node ]] || ERRORS='prebuilds/win32-x64/node.napi.node'