mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 12:57:46 +00:00
[redwood] Fix file permissions and add support for build script (#4999)
Some files require execution privileges, such as Prisma, so we must preserve the file mode. We also want redwood to behave the same as other frameworks and use `yarn build` if available.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
getNodeVersion,
|
getNodeVersion,
|
||||||
getSpawnOptions,
|
getSpawnOptions,
|
||||||
runNpmInstall,
|
runNpmInstall,
|
||||||
|
runPackageJsonScript,
|
||||||
execCommand,
|
execCommand,
|
||||||
FileBlob,
|
FileBlob,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
@@ -54,10 +55,6 @@ export async function build({
|
|||||||
meta
|
meta
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
|
||||||
buildCommand = 'yarn rw db up --no-db-client --auto-approve && yarn rw build',
|
|
||||||
} = config;
|
|
||||||
|
|
||||||
if (meta.isDev) {
|
if (meta.isDev) {
|
||||||
debug('Detected @vercel/redwood dev, returning routes...');
|
debug('Detected @vercel/redwood dev, returning routes...');
|
||||||
|
|
||||||
@@ -79,10 +76,27 @@ export async function build({
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug('Running build command...');
|
debug('Running build command...');
|
||||||
await execCommand(buildCommand, {
|
const { buildCommand } = config;
|
||||||
...spawnOpts,
|
|
||||||
cwd: workPath,
|
const found =
|
||||||
});
|
typeof buildCommand === 'string'
|
||||||
|
? await execCommand(buildCommand, {
|
||||||
|
...spawnOpts,
|
||||||
|
cwd: workPath,
|
||||||
|
})
|
||||||
|
: await runPackageJsonScript(
|
||||||
|
workPath,
|
||||||
|
['vercel-build', 'build'],
|
||||||
|
spawnOpts
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
throw new Error(
|
||||||
|
`Missing required "${
|
||||||
|
buildCommand || 'vercel-build'
|
||||||
|
}" script in "${entrypoint}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const apiDistPath = join(workPath, 'api', 'dist', 'functions');
|
const apiDistPath = join(workPath, 'api', 'dist', 'functions');
|
||||||
const webDistPath = join(workPath, 'web', 'dist');
|
const webDistPath = join(workPath, 'web', 'dist');
|
||||||
@@ -119,9 +133,11 @@ export async function build({
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
dependencies.forEach(fsPath => {
|
for (const fsPath of dependencies) {
|
||||||
lambdaFiles[relative(workPath, fsPath)] = new FileFsRef({ fsPath });
|
lambdaFiles[relative(workPath, fsPath)] = await FileFsRef.fromFsPath({
|
||||||
});
|
fsPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
lambdaFiles[relative(workPath, fileFsRef.fsPath)] = fileFsRef;
|
lambdaFiles[relative(workPath, fileFsRef.fsPath)] = fileFsRef;
|
||||||
|
|
||||||
|
|||||||
26
packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/permission.js
vendored
Executable file
26
packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/permission.js
vendored
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
const {
|
||||||
|
promises: { access },
|
||||||
|
constants: { X_OK },
|
||||||
|
} = require('fs')
|
||||||
|
|
||||||
|
async function isExecutable(fsPath) {
|
||||||
|
console.log(`Testing is file is executable: ${fsPath}`)
|
||||||
|
try {
|
||||||
|
await access(fsPath, X_OK)
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handler() {
|
||||||
|
const isExec = await isExecutable(module.id)
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {},
|
||||||
|
body: `File is executable: ${isExec}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { handler }
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
"web"
|
"web"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "rw db up --no-db-client --auto-approve && rw build"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redwoodjs/core": "0.15.0"
|
"@redwoodjs/core": "0.15.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
"headers": { "Accept": "application/json" },
|
"headers": { "Accept": "application/json" },
|
||||||
"body": { "query": "{ redwood { version } }" },
|
"body": { "query": "{ redwood { version } }" },
|
||||||
"mustContain": "0.15.0"
|
"mustContain": "0.15.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/api/permission",
|
||||||
|
"mustContain": "File is executable: true"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const { createHash } = require('crypto');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const _fetch = require('node-fetch');
|
const _fetch = require('node-fetch');
|
||||||
const fetch = require('./fetch-retry.js');
|
const fetch = require('./fetch-retry.js');
|
||||||
|
const fileModeSymbol = Symbol('fileMode');
|
||||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
async function nowDeploy(bodies, randomness, uploadNowJson) {
|
async function nowDeploy(bodies, randomness, uploadNowJson) {
|
||||||
@@ -14,7 +15,9 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
sha: digestOfFile(bodies[n]),
|
sha: digestOfFile(bodies[n]),
|
||||||
size: bodies[n].length,
|
size: bodies[n].length,
|
||||||
file: n,
|
file: n,
|
||||||
mode: path.extname(n) === '.sh' ? 0o100755 : 0o100644,
|
mode:
|
||||||
|
bodies[n][fileModeSymbol] ||
|
||||||
|
(path.extname(n) === '.sh' ? 0o100755 : 0o100644),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { FORCE_BUILD_IN_REGION, NOW_DEBUG, VERCEL_DEBUG } = process.env;
|
const { FORCE_BUILD_IN_REGION, NOW_DEBUG, VERCEL_DEBUG } = process.env;
|
||||||
@@ -80,9 +83,7 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function digestOfFile(body) {
|
function digestOfFile(body) {
|
||||||
return createHash('sha1')
|
return createHash('sha1').update(body).digest('hex');
|
||||||
.update(body)
|
|
||||||
.digest('hex');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function filePost(body, digest) {
|
async function filePost(body, digest) {
|
||||||
@@ -247,4 +248,5 @@ module.exports = {
|
|||||||
fetchWithAuth,
|
fetchWithAuth,
|
||||||
nowDeploy,
|
nowDeploy,
|
||||||
fetchTokenWithRetry,
|
fetchTokenWithRetry,
|
||||||
|
fileModeSymbol,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const glob = require('util').promisify(require('glob'));
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
const fetch = require('./fetch-retry.js');
|
const fetch = require('./fetch-retry.js');
|
||||||
const { nowDeploy } = require('./now-deploy.js');
|
const { nowDeploy, fileModeSymbol } = require('./now-deploy.js');
|
||||||
|
|
||||||
async function packAndDeploy(builderPath) {
|
async function packAndDeploy(builderPath) {
|
||||||
await spawnAsync('npm', ['--loglevel', 'warn', 'pack'], {
|
await spawnAsync('npm', ['--loglevel', 'warn', 'pack'], {
|
||||||
@@ -37,6 +37,7 @@ async function testDeployment(
|
|||||||
const bodies = globResult.reduce((b, f) => {
|
const bodies = globResult.reduce((b, f) => {
|
||||||
const r = path.relative(fixturePath, f);
|
const r = path.relative(fixturePath, f);
|
||||||
b[r] = fs.readFileSync(f);
|
b[r] = fs.readFileSync(f);
|
||||||
|
b[r][fileModeSymbol] = fs.statSync(f).mode;
|
||||||
return b;
|
return b;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user