diff --git a/cli/src/execDocker/docker.ts b/cli/src/execDocker/docker.ts index 9a0adc43..f5bbc8ec 100644 --- a/cli/src/execDocker/docker.ts +++ b/cli/src/execDocker/docker.ts @@ -2,6 +2,7 @@ import Docker from 'dockerode'; import os from 'os'; import { readdir } from 'fs/promises'; import { createSigintAbortSignal } from '../utils/abortController.js'; +import { CONFIG_FILE } from '../config/config.js'; type ProgressEvent = { stream?: string }; type FinishOutputRow = { error?: string }; @@ -31,9 +32,10 @@ export async function dockerBuild({ const osType = os.type(); const contextPath = process.cwd(); // Use current working directory + const contextFiles = await readdir(contextPath); const buildArgs = { context: contextPath, - src: await readdir(contextPath), // Include all files of the context + src: contextFiles.filter((fileName) => fileName !== CONFIG_FILE), // exclude config file from build context even if not in dockerignore }; // by default force to build amd64 image which is architecture used in iExec workers diff --git a/cli/templates/JavaScript/.dockerignore b/cli/templates/JavaScript/.dockerignore index 534b6e83..107a167e 100644 --- a/cli/templates/JavaScript/.dockerignore +++ b/cli/templates/JavaScript/.dockerignore @@ -1,3 +1,8 @@ +################ IMPORTANT NOTICE ################### +# Do not modify this file # +# Any modifications might not be taken into account # +##################################################### + # iapp config file may contain secrets iapp.config.json diff --git a/cli/templates/JavaScript/Dockerfile b/cli/templates/JavaScript/Dockerfile index f73b451d..5b23f2be 100644 --- a/cli/templates/JavaScript/Dockerfile +++ b/cli/templates/JavaScript/Dockerfile @@ -2,5 +2,5 @@ FROM node:22-alpine3.21 WORKDIR /app COPY package*.json ./ RUN npm ci -COPY . . +COPY src/ ./src/ ENTRYPOINT ["node", "--disable-wasm-trap-handler", "/app/src/app.js"] diff --git a/cli/templates/Python3.13/.dockerignore b/cli/templates/Python3.13/.dockerignore index 2803998c..a210cf7a 100644 --- a/cli/templates/Python3.13/.dockerignore +++ b/cli/templates/Python3.13/.dockerignore @@ -1,3 +1,8 @@ +################ IMPORTANT NOTICE ################### +# Do not modify this file # +# Any modifications might not be taken into account # +##################################################### + # iapp config file may contain secrets iapp.config.json diff --git a/cli/templates/Python3.13/Dockerfile b/cli/templates/Python3.13/Dockerfile index 9dbe45c4..ee1005cd 100644 --- a/cli/templates/Python3.13/Dockerfile +++ b/cli/templates/Python3.13/Dockerfile @@ -2,5 +2,5 @@ FROM python:3.13.3-alpine3.21 WORKDIR /app COPY requirements.txt ./ RUN pip install -r requirements.txt -COPY . . +COPY src/ ./src/ ENTRYPOINT ["python3", "/app/src/app.py"] diff --git a/cli/test/iapp.test.ts b/cli/test/iapp.test.ts index 0d2e50bb..5539b1f9 100644 --- a/cli/test/iapp.test.ts +++ b/cli/test/iapp.test.ts @@ -1,5 +1,5 @@ import { test, beforeEach, after, afterEach, describe } from 'node:test'; -import { join } from 'node:path'; +import { dirname, join } from 'node:path'; import assert from 'node:assert/strict'; import { render, cleanup } from 'cli-testing-library'; import { @@ -10,6 +10,8 @@ import { removeTestDir, retry, } from './test-utils.ts'; +import { fileURLToPath } from 'node:url'; +import { readFile, rm, writeFile } from 'node:fs/promises'; // Final cleanup code after all tests after(async () => { @@ -42,10 +44,16 @@ test('iapp help command works', async () => { }); test('iapp -v command works', async () => { + const __dirname = dirname(fileURLToPath(import.meta.url)); + const packageJson = JSON.parse( + await readFile(join(__dirname, '../package.json'), 'utf-8') + ); + const { version } = packageJson; + const { findByText, debug, clear } = await render(IAPP_COMMAND, ['-v'], { cwd: testDir, }); - await findByText('1.3.3'); + await findByText(version); // debug(); clear(); }); @@ -123,8 +131,6 @@ describe('JavaScript iApp', () => { await checkDockerImageContent({ dockerImageId, expectedFiles: [ - 'Dockerfile', - 'README.md', 'node_modules', 'package-lock.json', 'package.json', @@ -194,8 +200,6 @@ describe('JavaScript iApp', () => { await checkDockerImageContent({ dockerImageId, expectedFiles: [ - 'Dockerfile', - 'README.md', 'node_modules', 'package-lock.json', 'package.json', @@ -254,7 +258,7 @@ describe('Python iApp', () => { // check built docker image content await checkDockerImageContent({ dockerImageId, - expectedFiles: ['Dockerfile', 'README.md', 'requirements.txt', 'src'], + expectedFiles: ['requirements.txt', 'src'], }); }); }); @@ -318,8 +322,88 @@ describe('Python iApp', () => { // check built docker image content await checkDockerImageContent({ dockerImageId, - expectedFiles: ['Dockerfile', 'README.md', 'requirements.txt', 'src'], + expectedFiles: ['requirements.txt', 'src'], + }); + }); + }); + }); +}); + +describe('Custom app', () => { + describe('iapp test', () => { + const projectName = 'test-iapp'; + + // Initialize a test iApp project before each test + beforeEach(async () => { + await initIappProject({ + testDir, + projectName, + template: 'JavaScript', + projectType: 'Hello World', + }); + }); + + test('iapp test command works', async () => { + // removed .dockerignore + await rm(join(testDir, projectName, '.dockerignore')); + // changed Dockerfile to a custom one + const dockerfileContent = await readFile( + join(testDir, projectName, 'Dockerfile'), + 'utf-8' + ); + const customDockerfileContent = dockerfileContent.replace( + 'COPY src/ ./src/', + 'COPY . ./' + ); + await writeFile( + join(testDir, projectName, 'Dockerfile'), + customDockerfileContent, + 'utf-8' + ); + + const { findByText, debug, clear, userEvent, getStdallStr } = + await render(IAPP_COMMAND, ['test'], { + cwd: join(testDir, projectName), }); + // wait for docker build and test run + await retry(() => findByText('Would you like to see the app logs?'), { + retries: 8, + delay: 3000, + }); + // extract docker image id from stdout + const std = getStdallStr(); + const dockerImageIdMatch = std.match( + /App docker image built \(sha256:[a-f0-9]{64}\)/ + ); + assert.ok(dockerImageIdMatch, 'Docker image ID not found in output'); + const dockerImageId = dockerImageIdMatch![0].split('(')[1].slice(0, -1); + + // debug(); + clear(); + userEvent.keyboard('n'); + await findByText('Would you like to see the result?'); + // debug(); + clear(); + userEvent.keyboard('n'); + await findByText('When ready run iapp deploy'); + // debug(); + clear(); + + // check built docker image content + await checkDockerImageContent({ + dockerImageId, + expectedFiles: [ + 'Dockerfile', + 'README.md', + 'cache', + 'input', + 'mock', + 'node_modules', + 'output', + 'package-lock.json', + 'package.json', + 'src', + ], }); }); });