cloudron 8.0.1 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cloudron +8 -12
- package/package.json +1 -1
- package/src/actions.js +104 -34
- package/src/appstore-actions.js +8 -56
- package/src/build-actions.js +4 -1
- package/src/templates/flask/CloudronManifest.json +25 -0
- package/src/templates/flask/Dockerfile +16 -0
- package/src/templates/flask/app/__init__.py +5 -0
- package/src/templates/flask/app/views.py +5 -0
- package/src/templates/flask/dockerignore +5 -0
- package/src/templates/flask/requirements.txt +1 -0
- package/src/templates/flask/run.py +4 -0
- package/src/templates/flask/start.sh +8 -0
- package/src/templates/flask/supervisor/uwsgi.conf +12 -0
- package/src/templates/flask/template.json +3 -0
- package/src/templates/flask/uwsgi.ini +6 -0
- package/src/templates/lamp/CloudronManifest.json +26 -0
- package/src/templates/lamp/Dockerfile +27 -0
- package/src/templates/lamp/apache/app.conf +17 -0
- package/src/templates/lamp/apache/mpm_prefork.conf +7 -0
- package/src/templates/lamp/dockerignore +4 -0
- package/src/templates/lamp/index.php +18 -0
- package/src/templates/lamp/start.sh +9 -0
- package/src/templates/lamp/template.json +3 -0
- package/src/templates/lamp-composer/CloudronManifest.json +26 -0
- package/src/templates/lamp-composer/Dockerfile +30 -0
- package/src/templates/lamp-composer/apache/app.conf +17 -0
- package/src/templates/lamp-composer/apache/mpm_prefork.conf +7 -0
- package/src/templates/lamp-composer/composer.json +8 -0
- package/src/templates/lamp-composer/dockerignore +5 -0
- package/src/templates/lamp-composer/public/index.php +20 -0
- package/src/templates/lamp-composer/start.sh +9 -0
- package/src/templates/lamp-composer/template.json +3 -0
- package/src/templates/lemp/CloudronManifest.json +26 -0
- package/src/templates/lemp/Dockerfile +27 -0
- package/src/templates/lemp/dockerignore +4 -0
- package/src/templates/lemp/index.php +18 -0
- package/src/templates/lemp/nginx/app.conf +22 -0
- package/src/templates/lemp/start.sh +11 -0
- package/src/templates/lemp/template.json +3 -0
- package/src/templates/nextjs/CloudronManifest.json +25 -0
- package/src/templates/nextjs/Dockerfile +14 -0
- package/src/templates/nextjs/app/layout.js +12 -0
- package/src/templates/nextjs/app/page.js +7 -0
- package/src/templates/nextjs/dockerignore +5 -0
- package/src/templates/nextjs/next.config.mjs +6 -0
- package/src/templates/nextjs/package.json +15 -0
- package/src/templates/nextjs/start.sh +9 -0
- package/src/templates/nextjs/template.json +3 -0
- package/src/templates/nodejs/CloudronManifest.json +25 -0
- package/src/templates/nodejs/Dockerfile +10 -0
- package/src/templates/nodejs/dockerignore +4 -0
- package/src/templates/nodejs/package.json +11 -0
- package/src/templates/nodejs/server.js +12 -0
- package/src/templates/nodejs/start.sh +9 -0
- package/src/templates/nodejs/template.json +3 -0
- package/src/templates/nodejs-mongodb/CloudronManifest.json +26 -0
- package/src/templates/nodejs-mongodb/Dockerfile +10 -0
- package/src/templates/nodejs-mongodb/dockerignore +4 -0
- package/src/templates/nodejs-mongodb/package.json +14 -0
- package/src/templates/nodejs-mongodb/server.js +24 -0
- package/src/templates/nodejs-mongodb/start.sh +9 -0
- package/src/templates/nodejs-mongodb/template.json +3 -0
- package/src/templates/rails/CloudronManifest.json +26 -0
- package/src/templates/rails/Dockerfile +16 -0
- package/src/templates/rails/Gemfile +5 -0
- package/src/templates/rails/config/database.yml +4 -0
- package/src/templates/rails/config/puma.rb +9 -0
- package/src/templates/rails/dockerignore +7 -0
- package/src/templates/rails/public/index.html +12 -0
- package/src/templates/rails/start.sh +14 -0
- package/src/templates/rails/supervisor/puma.conf +12 -0
- package/src/templates/rails/template.json +3 -0
- package/src/templates/static/CloudronManifest.json +25 -0
- package/src/templates/static/Dockerfile +17 -0
- package/src/templates/static/apache/app.conf +13 -0
- package/src/templates/static/apache/mpm_prefork.conf +7 -0
- package/src/templates/static/dockerignore +3 -0
- package/src/templates/static/public/index.html +11 -0
- package/src/templates/static/start.sh +7 -0
- package/src/templates/static/template.json +3 -0
- package/test/test.js +1 -1
- /package/src/templates/{CloudronManifest.appstore.json.ejs → default/CloudronManifest.json.ejs} +0 -0
- /package/src/templates/{CloudronManifest.json.ejs → default/CloudronManifest.minimal.json.ejs} +0 -0
- /package/src/templates/{Dockerfile.ejs → default/Dockerfile.ejs} +0 -0
- /package/src/templates/{dockerignore.ejs → default/dockerignore.ejs} +0 -0
- /package/src/templates/{start.sh.ejs → default/start.sh.ejs} +0 -0
package/bin/cloudron
CHANGED
|
@@ -27,17 +27,7 @@ program.option('--server <server>', 'Cloudron domain')
|
|
|
27
27
|
.option('--no-wait', 'Do not wait for the operation to finish');
|
|
28
28
|
|
|
29
29
|
const appstoreCommand = program.command('appstore').description('Commands for publishing to the Appstore')
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
appstoreCommand.command('login')
|
|
33
|
-
.description('Login to the appstore')
|
|
34
|
-
.option('-e, --email <email>', 'Email address')
|
|
35
|
-
.option('-p, --password <password>', 'Password (unsafe)')
|
|
36
|
-
.action(appstoreActions.login);
|
|
37
|
-
|
|
38
|
-
appstoreCommand.command('logout')
|
|
39
|
-
.description('Logout from the appstore')
|
|
40
|
-
.action(appstoreActions.logout);
|
|
30
|
+
.requiredOption('--appstore-token <token>', 'AppStore token');
|
|
41
31
|
|
|
42
32
|
appstoreCommand.command('info')
|
|
43
33
|
.description('List info of published app')
|
|
@@ -264,7 +254,9 @@ program.command('inspect')
|
|
|
264
254
|
|
|
265
255
|
program.command('init')
|
|
266
256
|
.description('Creates a new CloudronManifest.json and Dockerfile')
|
|
267
|
-
.option('--
|
|
257
|
+
.option('--minimal', 'Minimal template')
|
|
258
|
+
.option('--template <name>', 'Use a project template (use --list-templates to see all)')
|
|
259
|
+
.option('--list-templates', 'List available templates')
|
|
268
260
|
.action(actions.init);
|
|
269
261
|
|
|
270
262
|
program.command('install')
|
|
@@ -281,6 +273,8 @@ program.command('install')
|
|
|
281
273
|
.option('--debug [cmd...]', 'Enable debug mode', false)
|
|
282
274
|
.option('--readonly', 'Mount filesystem readonly. Default is read/write in debug mode.')
|
|
283
275
|
.option('--env <KEY=value...>', 'Set environment variables. e.g X=1 Y=2')
|
|
276
|
+
.option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectBuildArgs, [])
|
|
277
|
+
.option('-f, --file <dockerfile>', 'Name of the Dockerfile')
|
|
284
278
|
.action(actions.install);
|
|
285
279
|
|
|
286
280
|
program.command('list')
|
|
@@ -405,6 +399,8 @@ program.command('update')
|
|
|
405
399
|
.option('--image <docker image>', 'Docker image')
|
|
406
400
|
.option('--no-backup', 'Skip backup [false]')
|
|
407
401
|
.option('--no-force', 'Match appstore id and manifest id before updating', true)
|
|
402
|
+
.option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectBuildArgs, [])
|
|
403
|
+
.option('-f, --file <dockerfile>', 'Name of the Dockerfile')
|
|
408
404
|
.action(actions.update);
|
|
409
405
|
|
|
410
406
|
const versionsCommand = program.command('versions').description('Commands for publishing community packages');
|
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -224,7 +224,7 @@ async function waitForFinishInstallation(appId, taskId, options) {
|
|
|
224
224
|
|
|
225
225
|
if (response.body.installationState !== 'installed') throw new Error(`Installation failed: ${response.body.error ? response.body.error.message : ''}`);
|
|
226
226
|
|
|
227
|
-
await waitForHealthy(appId, options);
|
|
227
|
+
if (!options.debug) await waitForHealthy(appId, options);
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
async function waitForFinishBackup(appId, taskId, options) {
|
|
@@ -683,7 +683,6 @@ async function install(localOptions, cmd) {
|
|
|
683
683
|
cmd: debugCmd
|
|
684
684
|
};
|
|
685
685
|
data.memoryLimit = -1;
|
|
686
|
-
options.wait = false; // in debug mode, health check never succeeds
|
|
687
686
|
}
|
|
688
687
|
|
|
689
688
|
if (!options.appstoreId && !options.versionsUrl && manifest && manifest.icon) {
|
|
@@ -712,6 +711,10 @@ async function install(localOptions, cmd) {
|
|
|
712
711
|
if (data.sso !== undefined) request.field('sso', String(data.sso));
|
|
713
712
|
if (data.debugMode) request.field('debugMode', JSON.stringify(data.debugMode));
|
|
714
713
|
if (data.icon) request.field('icon', data.icon);
|
|
714
|
+
const buildConfig = {};
|
|
715
|
+
if (options.buildArg && options.buildArg.length) buildConfig.buildArgs = options.buildArg;
|
|
716
|
+
if (options.file) buildConfig.dockerfileName = options.file;
|
|
717
|
+
if (Object.keys(buildConfig).length) request.field('buildConfig', JSON.stringify(buildConfig));
|
|
715
718
|
request.attach('sourceArchive', sourceArchiveFilePath);
|
|
716
719
|
|
|
717
720
|
response = await request;
|
|
@@ -903,6 +906,10 @@ async function update(localOptions, cmd) {
|
|
|
903
906
|
request.field('skipNotification', String(data.skipNotification));
|
|
904
907
|
request.field('force', String(data.force));
|
|
905
908
|
if (data.icon) request.field('icon', data.icon);
|
|
909
|
+
const buildConfig = {};
|
|
910
|
+
if (options.buildArg && options.buildArg.length) buildConfig.buildArgs = options.buildArg;
|
|
911
|
+
if (options.file) buildConfig.dockerfileName = options.file;
|
|
912
|
+
if (Object.keys(buildConfig).length) request.field('buildConfig', JSON.stringify(buildConfig));
|
|
906
913
|
request.attach('sourceArchive', sourceArchiveFilePath);
|
|
907
914
|
|
|
908
915
|
response = await request;
|
|
@@ -1504,7 +1511,6 @@ function runExecSession(appId, execId, { stdin, stdout, stderr, tty }, options)
|
|
|
1504
1511
|
if (stdout !== process.stdout && typeof stdout.end === 'function') {
|
|
1505
1512
|
stdout.on('close', () => resolve());
|
|
1506
1513
|
stdout.end();
|
|
1507
|
-
stdout.resume(); // drain readable side of Duplex/Transform streams so autoDestroy emits 'close'
|
|
1508
1514
|
} else {
|
|
1509
1515
|
setImmediate(resolve);
|
|
1510
1516
|
}
|
|
@@ -1912,52 +1918,116 @@ async function syncPull(remoteDir, localDir, localOptions, cmd) {
|
|
|
1912
1918
|
}
|
|
1913
1919
|
}
|
|
1914
1920
|
|
|
1921
|
+
function listTemplates() {
|
|
1922
|
+
const templatesDir = path.join(import.meta.dirname, 'templates');
|
|
1923
|
+
const entries = fs.readdirSync(templatesDir, { withFileTypes: true });
|
|
1924
|
+
|
|
1925
|
+
console.log();
|
|
1926
|
+
console.log('Available templates:');
|
|
1927
|
+
console.log();
|
|
1928
|
+
|
|
1929
|
+
for (const entry of entries) {
|
|
1930
|
+
if (!entry.isDirectory()) continue;
|
|
1931
|
+
|
|
1932
|
+
const metaPath = path.join(templatesDir, entry.name, 'template.json');
|
|
1933
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
1934
|
+
|
|
1935
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
1936
|
+
console.log(` ${entry.name.padEnd(15)} ${meta.description}`);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
console.log();
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
function copyTemplateDir(srcDir, destDir) {
|
|
1943
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
1944
|
+
|
|
1945
|
+
for (const entry of entries) {
|
|
1946
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
1947
|
+
|
|
1948
|
+
if (entry.name === 'template.json') continue;
|
|
1949
|
+
|
|
1950
|
+
const destName = entry.name === 'dockerignore' ? '.dockerignore' : entry.name;
|
|
1951
|
+
const destPath = path.join(destDir, destName);
|
|
1952
|
+
|
|
1953
|
+
if (entry.isDirectory()) {
|
|
1954
|
+
if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });
|
|
1955
|
+
copyTemplateDir(srcPath, destPath);
|
|
1956
|
+
} else {
|
|
1957
|
+
if (fs.existsSync(destPath)) {
|
|
1958
|
+
console.log(`${destName} already exists, skipping`);
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
fs.copyFileSync(srcPath, destPath);
|
|
1963
|
+
|
|
1964
|
+
if (destName === 'start.sh') fs.chmodSync(destPath, 0o0775);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1915
1969
|
function init(localOptions, cmd) {
|
|
1916
1970
|
const options = cmd.optsWithGlobals();
|
|
1971
|
+
|
|
1972
|
+
if (options.listTemplates) return listTemplates();
|
|
1973
|
+
|
|
1974
|
+
if (options.template && options.minimal) return exit('--template and --minimal are mutually exclusive');
|
|
1975
|
+
|
|
1917
1976
|
const manifestFilePath = locateManifest();
|
|
1918
1977
|
if (manifestFilePath && path.dirname(manifestFilePath) === process.cwd()) return exit('CloudronManifest.json already exists in current directory');
|
|
1919
1978
|
|
|
1920
|
-
|
|
1979
|
+
if (options.template) {
|
|
1980
|
+
const templateDir = path.join(import.meta.dirname, 'templates', options.template);
|
|
1981
|
+
const metaPath = path.join(templateDir, 'template.json');
|
|
1982
|
+
if (!fs.existsSync(metaPath)) {
|
|
1983
|
+
console.log(`Unknown template "${options.template}".`);
|
|
1984
|
+
return listTemplates();
|
|
1985
|
+
}
|
|
1921
1986
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
const startShTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/', 'start.sh.ejs'), 'utf8');
|
|
1987
|
+
copyTemplateDir(templateDir, process.cwd());
|
|
1988
|
+
} else {
|
|
1989
|
+
const manifestTemplateFilename = !options.minimal ? 'CloudronManifest.json.ejs' : 'CloudronManifest.minimal.json.ejs';
|
|
1926
1990
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
};
|
|
1991
|
+
const manifestTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', manifestTemplateFilename), 'utf8');
|
|
1992
|
+
const dockerfileTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'Dockerfile.ejs'), 'utf8');
|
|
1993
|
+
const dockerignoreTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'dockerignore.ejs'), 'utf8');
|
|
1994
|
+
const startShTemplate = fs.readFileSync(path.join(import.meta.dirname, 'templates/default/', 'start.sh.ejs'), 'utf8');
|
|
1932
1995
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1996
|
+
const data = {
|
|
1997
|
+
version: '0.1.0',
|
|
1998
|
+
title: 'App title',
|
|
1999
|
+
httpPort: 3000
|
|
2000
|
+
};
|
|
1935
2001
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
} else {
|
|
1939
|
-
const dockerfile = ejs.render(dockerfileTemplate, data);
|
|
1940
|
-
fs.writeFileSync('Dockerfile', dockerfile, 'utf8');
|
|
1941
|
-
}
|
|
2002
|
+
const manifest = ejs.render(manifestTemplate, data);
|
|
2003
|
+
fs.writeFileSync('CloudronManifest.json', manifest, 'utf8');
|
|
1942
2004
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2005
|
+
if (fs.existsSync('Dockerfile')) {
|
|
2006
|
+
console.log('Dockerfile already exists, skipping');
|
|
2007
|
+
} else {
|
|
2008
|
+
const dockerfile = ejs.render(dockerfileTemplate, data);
|
|
2009
|
+
fs.writeFileSync('Dockerfile', dockerfile, 'utf8');
|
|
2010
|
+
}
|
|
1949
2011
|
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2012
|
+
if (fs.existsSync('.dockerignore')) {
|
|
2013
|
+
console.log('.dockerignore already exists, skipping');
|
|
2014
|
+
} else {
|
|
2015
|
+
const dockerignore = ejs.render(dockerignoreTemplate, data);
|
|
2016
|
+
fs.writeFileSync('.dockerignore', dockerignore, 'utf8');
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
if (fs.existsSync('start.sh')) {
|
|
2020
|
+
console.log('start.sh already exists, skipping');
|
|
2021
|
+
} else {
|
|
2022
|
+
const startSh = ejs.render(startShTemplate, {});
|
|
2023
|
+
fs.writeFileSync('start.sh', startSh, 'utf8');
|
|
2024
|
+
fs.chmodSync('start.sh', 0o0775);
|
|
2025
|
+
}
|
|
1956
2026
|
}
|
|
1957
2027
|
|
|
1958
2028
|
fs.copyFileSync(path.join(import.meta.dirname, 'templates/', 'logo.png'), 'logo.png');
|
|
1959
2029
|
|
|
1960
|
-
if (options.
|
|
2030
|
+
if (!options.minimal) {
|
|
1961
2031
|
if (!fs.existsSync('.gitignore')) fs.writeFileSync('.gitignore', 'node_modules/\n', 'utf8');
|
|
1962
2032
|
if (!fs.existsSync('DESCRIPTION.md')) fs.writeFileSync('DESCRIPTION.md', '## About\n\nThis app changes everything\n\n', 'utf8');
|
|
1963
2033
|
if (!fs.existsSync('POSTINSTALL.md')) fs.writeFileSync('POSTINSTALL.md', 'Post installation information\n\n', 'utf8');
|
package/src/appstore-actions.js
CHANGED
|
@@ -13,7 +13,7 @@ import Table from 'easy-table';
|
|
|
13
13
|
const NO_MANIFEST_FOUND_ERROR_STRING = 'No CloudronManifest.json found';
|
|
14
14
|
|
|
15
15
|
function requestError(response) {
|
|
16
|
-
if (response.status === 401) return 'Invalid token.
|
|
16
|
+
if (response.status === 401) return 'Invalid token.';
|
|
17
17
|
|
|
18
18
|
return `${response.status} message: ${response.body?.message || response.text || JSON.stringify(response.body)}`; // body is sometimes just a string like in 401
|
|
19
19
|
}
|
|
@@ -30,10 +30,6 @@ function createRequest(method, apiPath, options) {
|
|
|
30
30
|
return request;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function createUrl(api) {
|
|
34
|
-
return config.appStoreOrigin() + api;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
33
|
// the app argument allows us in the future to get by name or id
|
|
38
34
|
async function getAppstoreId(appstoreId) {
|
|
39
35
|
if (appstoreId) return appstoreId.split('@');
|
|
@@ -47,54 +43,6 @@ async function getAppstoreId(appstoreId) {
|
|
|
47
43
|
return [manifest.id, manifest.version];
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
async function authenticate(options) { // maybe we can use options.token to valid using a profile call?
|
|
51
|
-
if (!options.hideBanner) {
|
|
52
|
-
const webDomain = config.appStoreOrigin().replace('https://api.', '');
|
|
53
|
-
console.log(`${webDomain} login` + ` (If you do not have one, sign up at https://${webDomain}/console.html#/register)`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const email = options.email || await readline.question('Email: ', {});
|
|
57
|
-
const password = options.password || await readline.question('Password: ', { noEchoBack: true });
|
|
58
|
-
|
|
59
|
-
config.setAppStoreToken(null);
|
|
60
|
-
|
|
61
|
-
const response = await superagent.post(createUrl('/api/v1/login')).auth(email, password).send({ totpToken: options.totpToken }).ok(() => true);
|
|
62
|
-
if (response.status === 401 && response.body.message.indexOf('TOTP') !== -1) {
|
|
63
|
-
if (response.body.message === 'TOTP token missing') console.log('A 2FA TOTP Token is required for this account.');
|
|
64
|
-
|
|
65
|
-
options.totpToken = await readline.question('2FA token: ', {});
|
|
66
|
-
options.email = email;
|
|
67
|
-
options.password = password;
|
|
68
|
-
options.hideBanner = true;
|
|
69
|
-
|
|
70
|
-
return await authenticate(options); // try again with top set
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (response.status !== 200) {
|
|
74
|
-
console.log('Login failed.');
|
|
75
|
-
|
|
76
|
-
options.hideBanner = true;
|
|
77
|
-
options.email = '';
|
|
78
|
-
options.password = '';
|
|
79
|
-
|
|
80
|
-
return await authenticate(options);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
config.setAppStoreToken(response.body.accessToken);
|
|
84
|
-
|
|
85
|
-
console.log('Login successful.');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function login(localOptions, cmd) {
|
|
89
|
-
const options = cmd.optsWithGlobals();
|
|
90
|
-
await authenticate(options);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function logout() {
|
|
94
|
-
config.setAppStoreToken(null);
|
|
95
|
-
console.log('Done.');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
46
|
async function info(localOptions, cmd) {
|
|
99
47
|
const options = cmd.optsWithGlobals();
|
|
100
48
|
const [id, version] = await getAppstoreId(options.appstoreId);
|
|
@@ -238,19 +186,25 @@ async function verifyManifest(localOptions, cmd) {
|
|
|
238
186
|
const appConfig = config.getCwdConfig(sourceDir);
|
|
239
187
|
|
|
240
188
|
// image can be passed in options for buildbot
|
|
189
|
+
let missingDockerImage = false;
|
|
241
190
|
if (options.image) {
|
|
242
191
|
manifest.dockerImage = options.image;
|
|
243
192
|
} else {
|
|
244
193
|
manifest.dockerImage = appConfig.dockerImage;
|
|
245
194
|
}
|
|
246
195
|
|
|
247
|
-
if (!manifest.dockerImage)
|
|
196
|
+
if (!manifest.dockerImage) {
|
|
197
|
+
missingDockerImage = true;
|
|
198
|
+
manifest.dockerImage = `cloudron/${manifest.id}:${manifest.version}`;
|
|
199
|
+
}
|
|
248
200
|
|
|
249
201
|
// ensure we remove the docker hub handle
|
|
250
202
|
if (manifest.dockerImage.indexOf('docker.io/') === 0) manifest.dockerImage = manifest.dockerImage.slice('docker.io/'.length);
|
|
251
203
|
|
|
252
204
|
const error = manifestFormat.checkAppstoreRequirements(manifest);
|
|
253
205
|
if (error) return exit(error);
|
|
206
|
+
|
|
207
|
+
if (missingDockerImage) return exit('No docker image found, run `cloudron build` first');
|
|
254
208
|
}
|
|
255
209
|
|
|
256
210
|
async function checkDockerHub(dockerImage) {
|
|
@@ -471,8 +425,6 @@ async function notify() {
|
|
|
471
425
|
}
|
|
472
426
|
|
|
473
427
|
export default {
|
|
474
|
-
login,
|
|
475
|
-
logout,
|
|
476
428
|
info,
|
|
477
429
|
listVersions,
|
|
478
430
|
submit,
|
package/src/build-actions.js
CHANGED
|
@@ -313,7 +313,10 @@ async function build(localOptions, cmd) {
|
|
|
313
313
|
config.setCwdConfig(sourceDir, appConfig);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
const gitCommit = safe.child_process.execSync('git rev-parse HEAD', { encoding: 'utf8' })?.trim(); // when the build gets saved, save the gitCommit also
|
|
317
|
+
if (safe.error) console.log('No git repository found. Continuing without commit sha info.');
|
|
318
|
+
|
|
319
|
+
appConfig.gitCommit = gitCommit || '';
|
|
317
320
|
if (buildServiceConfig.type === 'remote' && buildServiceConfig.url) {
|
|
318
321
|
console.log('Building using remote build service at %s', buildServiceConfig.url);
|
|
319
322
|
await buildRemote(manifest, sourceDir, appConfig, options, buildServiceConfig);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"upstreamVersion": "",
|
|
5
|
+
"minBoxVersion": "9.0.0",
|
|
6
|
+
"title": "",
|
|
7
|
+
"author": "",
|
|
8
|
+
"description": "file://DESCRIPTION.md",
|
|
9
|
+
"tagline": "",
|
|
10
|
+
"website": "",
|
|
11
|
+
"contactEmail": "",
|
|
12
|
+
"iconUrl": "file://logo.png",
|
|
13
|
+
"healthCheckPath": "/",
|
|
14
|
+
"mediaLinks": [],
|
|
15
|
+
"httpPort": 3000,
|
|
16
|
+
"tags": [],
|
|
17
|
+
"changelog": "file://CHANGELOG",
|
|
18
|
+
"postInstallMessage": "file://POSTINSTALL.md",
|
|
19
|
+
"documentationUrl": "",
|
|
20
|
+
"forumUrl": "",
|
|
21
|
+
"addons": {
|
|
22
|
+
"localstorage": {}
|
|
23
|
+
},
|
|
24
|
+
"manifestVersion": 2
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
|
2
|
+
|
|
3
|
+
RUN mkdir -p /app/code
|
|
4
|
+
WORKDIR /app/code
|
|
5
|
+
|
|
6
|
+
COPY . /app/code
|
|
7
|
+
|
|
8
|
+
RUN pip install -r requirements.txt
|
|
9
|
+
|
|
10
|
+
ADD supervisor/* /etc/supervisor/conf.d/
|
|
11
|
+
RUN ln -sf /run/supervisord.log /var/log/supervisor/supervisord.log
|
|
12
|
+
|
|
13
|
+
ADD uwsgi.ini /etc/uwsgi/apps-available/flask-uwsgi.ini
|
|
14
|
+
RUN ln -s /etc/uwsgi/apps-available/flask-uwsgi.ini /etc/uwsgi/apps-enabled/flask-uwsgi.ini
|
|
15
|
+
|
|
16
|
+
CMD ["/app/code/start.sh"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flask
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[program:uwsgi]
|
|
2
|
+
autorestart=true
|
|
3
|
+
autostart=true
|
|
4
|
+
command=uwsgi --master --workers 2 --no-orphans --ini /etc/uwsgi/apps-enabled/flask-uwsgi.ini
|
|
5
|
+
stdout_logfile=/dev/stdout
|
|
6
|
+
stdout_logfile_maxbytes=0
|
|
7
|
+
stderr_logfile=/dev/stderr
|
|
8
|
+
stderr_logfile_maxbytes=0
|
|
9
|
+
killasgroup=true
|
|
10
|
+
stopasgroup=true
|
|
11
|
+
stopsignal=QUIT
|
|
12
|
+
priority=100
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"upstreamVersion": "",
|
|
5
|
+
"minBoxVersion": "9.0.0",
|
|
6
|
+
"title": "",
|
|
7
|
+
"author": "",
|
|
8
|
+
"description": "file://DESCRIPTION.md",
|
|
9
|
+
"tagline": "",
|
|
10
|
+
"website": "",
|
|
11
|
+
"contactEmail": "",
|
|
12
|
+
"iconUrl": "file://logo.png",
|
|
13
|
+
"healthCheckPath": "/",
|
|
14
|
+
"mediaLinks": [],
|
|
15
|
+
"httpPort": 3000,
|
|
16
|
+
"tags": [],
|
|
17
|
+
"changelog": "file://CHANGELOG",
|
|
18
|
+
"postInstallMessage": "file://POSTINSTALL.md",
|
|
19
|
+
"documentationUrl": "",
|
|
20
|
+
"forumUrl": "",
|
|
21
|
+
"addons": {
|
|
22
|
+
"localstorage": {},
|
|
23
|
+
"mysql": {}
|
|
24
|
+
},
|
|
25
|
+
"manifestVersion": 2
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
FROM cloudron/base:5.0.0@sha256:04fd70dbd8ad6149c19de39e35718e024417c3e01dc9c6637eaf4a41ec4e596c
|
|
2
|
+
|
|
3
|
+
RUN mkdir -p /app/code
|
|
4
|
+
WORKDIR /app/code
|
|
5
|
+
|
|
6
|
+
RUN rm /etc/apache2/sites-enabled/*
|
|
7
|
+
RUN sed -e 's,^ErrorLog.*,ErrorLog "|/bin/cat",' -i /etc/apache2/apache2.conf
|
|
8
|
+
COPY apache/mpm_prefork.conf /etc/apache2/mods-available/mpm_prefork.conf
|
|
9
|
+
|
|
10
|
+
RUN a2disconf other-vhosts-access-log
|
|
11
|
+
COPY apache/app.conf /etc/apache2/sites-enabled/app.conf
|
|
12
|
+
RUN echo "Listen 3000" > /etc/apache2/ports.conf
|
|
13
|
+
|
|
14
|
+
RUN a2enmod php8.3
|
|
15
|
+
RUN crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_filesize 256M && \
|
|
16
|
+
crudini --set /etc/php/8.3/apache2/php.ini PHP upload_max_size 256M && \
|
|
17
|
+
crudini --set /etc/php/8.3/apache2/php.ini PHP post_max_size 256M && \
|
|
18
|
+
crudini --set /etc/php/8.3/apache2/php.ini PHP memory_limit 256M && \
|
|
19
|
+
crudini --set /etc/php/8.3/apache2/php.ini PHP max_execution_time 200 && \
|
|
20
|
+
crudini --set /etc/php/8.3/apache2/php.ini Session session.save_path /run/app/sessions && \
|
|
21
|
+
crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_probability 1 && \
|
|
22
|
+
crudini --set /etc/php/8.3/apache2/php.ini Session session.gc_divisor 100
|
|
23
|
+
|
|
24
|
+
COPY index.php start.sh /app/code/
|
|
25
|
+
RUN chown -R www-data.www-data /app/code
|
|
26
|
+
|
|
27
|
+
CMD [ "/app/code/start.sh" ]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<VirtualHost *:3000>
|
|
2
|
+
DocumentRoot /app/code
|
|
3
|
+
|
|
4
|
+
ErrorLog "|/bin/cat"
|
|
5
|
+
CustomLog "|/bin/cat" combined
|
|
6
|
+
|
|
7
|
+
<Directory /app/code/>
|
|
8
|
+
Options +FollowSymLinks
|
|
9
|
+
AllowOverride All
|
|
10
|
+
Require all granted
|
|
11
|
+
|
|
12
|
+
<Files "config.php">
|
|
13
|
+
Require all denied
|
|
14
|
+
</Files>
|
|
15
|
+
</Directory>
|
|
16
|
+
|
|
17
|
+
</VirtualHost>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
$host = getenv("CLOUDRON_MYSQL_HOST");
|
|
3
|
+
$port = getenv("CLOUDRON_MYSQL_PORT");
|
|
4
|
+
$username = getenv("CLOUDRON_MYSQL_USERNAME");
|
|
5
|
+
$password = getenv("CLOUDRON_MYSQL_PASSWORD");
|
|
6
|
+
$database = getenv("CLOUDRON_MYSQL_DATABASE");
|
|
7
|
+
|
|
8
|
+
echo "<h1>Hello from Cloudron!</h1>\n";
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
$dsn = "mysql:host=$host;port=$port;dbname=$database";
|
|
12
|
+
$pdo = new PDO($dsn, $username, $password);
|
|
13
|
+
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
14
|
+
echo "<p>MySQL connection successful.</p>\n";
|
|
15
|
+
} catch (PDOException $e) {
|
|
16
|
+
echo "<p>MySQL connection failed: " . htmlspecialchars($e->getMessage()) . "</p>\n";
|
|
17
|
+
}
|
|
18
|
+
?>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"upstreamVersion": "",
|
|
5
|
+
"minBoxVersion": "9.0.0",
|
|
6
|
+
"title": "",
|
|
7
|
+
"author": "",
|
|
8
|
+
"description": "file://DESCRIPTION.md",
|
|
9
|
+
"tagline": "",
|
|
10
|
+
"website": "",
|
|
11
|
+
"contactEmail": "",
|
|
12
|
+
"iconUrl": "file://logo.png",
|
|
13
|
+
"healthCheckPath": "/",
|
|
14
|
+
"mediaLinks": [],
|
|
15
|
+
"httpPort": 3000,
|
|
16
|
+
"tags": [],
|
|
17
|
+
"changelog": "file://CHANGELOG",
|
|
18
|
+
"postInstallMessage": "file://POSTINSTALL.md",
|
|
19
|
+
"documentationUrl": "",
|
|
20
|
+
"forumUrl": "",
|
|
21
|
+
"addons": {
|
|
22
|
+
"localstorage": {},
|
|
23
|
+
"mysql": {}
|
|
24
|
+
},
|
|
25
|
+
"manifestVersion": 2
|
|
26
|
+
}
|