Skip to content

Update test-e2e.yml #3015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: 'Run tests'
on:
pull_request:
branches: [main]
branches: [not_main] # do not run for now
schedule:
- cron: '0 6 * * *' # Run every day at 6am UTC
workflow_dispatch:
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ env:
# In older versions of Next.js both of these need to be set to enable junit reporting
DATADOG_TRACE_NEXTJS_TEST: true
DATADOG_API_KEY: foo
TEST_CONCURRENCY: 2
TEST_CONCURRENCY: 1
NEXT_E2E_TEST_TIMEOUT: 300000
NEXT_TELEMETRY_DISABLED: 1
NEXT_SKIP_NATIVE_POSTINSTALL: 1
Expand Down Expand Up @@ -59,16 +59,16 @@ jobs:
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION_SELECTORS=[${{ github.event.inputs.versions }}]
echo "group=[1, 2, 3, 4, 5, 6, 7, 8]" >> $GITHUB_OUTPUT
echo "total=8" >> $GITHUB_OUTPUT
echo "group=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]" >> $GITHUB_OUTPUT
echo "total=16" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" == "pull_request" ]; then
VERSION_SELECTORS=[\"latest\"]
echo "group=[1, 2, 3, 4, 5, 6, 7, 8]" >> $GITHUB_OUTPUT
echo "total=8" >> $GITHUB_OUTPUT
echo "group=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]" >> $GITHUB_OUTPUT
echo "total=16" >> $GITHUB_OUTPUT
else
VERSION_SELECTORS=[\"latest\",\"canary\"]
echo "group=[1, 2, 3, 4, 5, 6, 7, 8]" >> $GITHUB_OUTPUT
echo "total=8" >> $GITHUB_OUTPUT
echo "group=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]" >> $GITHUB_OUTPUT
echo "total=16" >> $GITHUB_OUTPUT
fi

VERSION_SPEC="["
Expand Down Expand Up @@ -205,6 +205,7 @@ jobs:
# one job may wait for deploys in other jobs (only one deploy may be in progress for
# a given alias at a time), resulting in cascading timeouts.
DEPLOY_ALIAS: vercel-next-e2e-${{ matrix.version_spec.selector }}-${{ matrix.group }}
# NODE_OPTIONS: "--require /home/runner/work/opennextjs-netlify/opennextjs-netlify/opennextjs-netlify/skip-integrations.cjs"
run: node run-tests.js -g ${{ matrix.group }}/${{ needs.setup.outputs.total }} -c ${TEST_CONCURRENCY} --type e2e
working-directory: ${{ env.next-path }}

Expand Down
17 changes: 17 additions & 0 deletions skip-integrations.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { http, HttpResponse, passthrough } = require('msw')
// eslint-disable-next-line import/extensions
const { setupServer } = require('msw/node')

const server = setupServer(
http.get(
'https://api.netlifysdk.com/team/:accountId/integrations/installations/meta/:siteId',
() => {
return HttpResponse.json([])
},
),
http.get('https://api.netlifysdk.com/site/:siteId/integrations/safe', () => {
return HttpResponse.json([])
}),
http.all(/.*/, () => passthrough()),
)
server.listen()
148 changes: 135 additions & 13 deletions tests/netlify-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ async function packNextRuntimeImpl() {
}

let packNextRuntimePromise: ReturnType<typeof packNextRuntimeImpl> | null = null
let nextRuntimePacked = false
function packNextRuntime() {
if (!packNextRuntimePromise) {
packNextRuntimePromise = packNextRuntimeImpl()
Expand All @@ -38,13 +39,58 @@ export class NextDeployInstance extends NextInstance {
private _shouldDeleteDeploy: boolean = false
private _isCurrentlyDeploying: boolean = false
private _deployOutput: string = ''
private _setupStartTime = Date.now()
private _intervalsToClear: NodeJS.Timeout[] = []

public get buildId() {
// get deployment ID via fetch since we can't access
// build artifacts directly
return this._buildId
}

private packNextRuntime() {
if (!packNextRuntimePromise) {
if (!nextRuntimePacked) {
this._deployOutput += this.getTimestampPrefix() + 'Pack Next Runtime ...\n'
}
packNextRuntimePromise = packNextRuntimeImpl()
packNextRuntimePromise.then(() => {
nextRuntimePacked = true
})
if (!nextRuntimePacked) {
this._deployOutput += this.getTimestampPrefix() + 'Pack Next Runtime DONE\n'
}
}

return packNextRuntimePromise
}

private clearIntervals() {
for (const interval of this._intervalsToClear) {
clearInterval(interval)
}
this._intervalsToClear = []
}

private getTimestampPrefix() {
return `[${new Date().toISOString()}] (+${((Date.now() - this._setupStartTime) / 1000).toFixed(3)}s) `
}

private ps(pid) {
const netlifyStatusPromise = execa('ps', ['-p', pid])

netlifyStatusPromise.stdout.on('data', this.handleOutput.bind(this))
netlifyStatusPromise.stderr.on('data', this.handleOutput.bind(this))
}

private handleOutput(chunk) {
const timestampPrefix = this.getTimestampPrefix()

this._deployOutput +=
(this._deployOutput === '' || this._deployOutput.endsWith('\n') ? timestampPrefix : '') +
chunk.toString().replace(/\n(?=.)/gm, `\n${timestampPrefix}`)
}

public async setup(parentSpan: Span) {
if (process.env.SITE_URL && process.env.BUILD_ID) {
require('console').log('Using existing deployment: ' + process.env.SITE_URL)
Expand All @@ -56,35 +102,45 @@ export class NextDeployInstance extends NextInstance {

this._isCurrentlyDeploying = true

const setupStartTime = Date.now()

this._deployOutput += this.getTimestampPrefix() + 'Setting up test dir ...\n'
// create the test site
await super.createTestDir({ parentSpan, skipInstall: true })
this._deployOutput += this.getTimestampPrefix() + 'Setting up test dir DONE\n'

// If the test fixture has node modules we need to move them aside then merge them in after

const nodeModules = path.join(this.testDir, 'node_modules')
const nodeModulesBak = `${nodeModules}.bak`

if (fs.existsSync(nodeModules)) {
this._deployOutput += this.getTimestampPrefix() + 'Rename node_modules ...\n'
await fs.rename(nodeModules, nodeModulesBak)
this._deployOutput += this.getTimestampPrefix() + 'Rename node_modules DONE\n'
}

const { runtimePackageName, runtimePackageTarballPath } = await packNextRuntime()
const { runtimePackageName, runtimePackageTarballPath } = await this.packNextRuntime()

// install dependencies
await execa('npm', ['i', runtimePackageTarballPath, '--legacy-peer-deps'], {
this._deployOutput += this.getTimestampPrefix() + 'Install dependencies ...\n'
const installResPromise = execa('npm', ['i', runtimePackageTarballPath, '--legacy-peer-deps'], {
cwd: this.testDir,
stdio: 'inherit',
})

installResPromise.stdout.on('data', this.handleOutput.bind(this))
installResPromise.stderr.on('data', this.handleOutput.bind(this))

await installResPromise
this._deployOutput += this.getTimestampPrefix() + 'Install dependencies DONE\n'

if (fs.existsSync(nodeModulesBak)) {
// move the contents of the fixture node_modules into the installed modules
this._deployOutput += this.getTimestampPrefix() + 'Move fixture node_modules ...\n'
for (const file of await fs.readdir(nodeModulesBak)) {
await fs.move(path.join(nodeModulesBak, file), path.join(nodeModules, file), {
overwrite: true,
})
}
this._deployOutput += this.getTimestampPrefix() + 'Move fixture node_modules DONE\n'
}

// use next runtime package installed by the test runner
Expand Down Expand Up @@ -117,7 +173,12 @@ export class NextDeployInstance extends NextInstance {

// ensure project is linked
try {
await execa('npx', ['netlify', 'status', '--json'])
const netlifyStatusPromise = execa('npx', ['netlify', 'status', '--json'])

netlifyStatusPromise.stdout.on('data', this.handleOutput.bind(this))
netlifyStatusPromise.stderr.on('data', this.handleOutput.bind(this))

await netlifyStatusPromise
} catch (err) {
if (err.message.includes("You don't appear to be in a folder that is linked to a site")) {
throw new Error(`Site is not linked. Please set "NETLIFY_AUTH_TOKEN" and "NETLIFY_SITE_ID"`)
Expand All @@ -144,15 +205,75 @@ export class NextDeployInstance extends NextInstance {
},
)

const handleOutput = (chunk) => {
this._deployOutput += chunk
}
this._deployOutput +=
this.getTimestampPrefix() + `Started deploy, PID: ${deployResPromise.pid}\n`
require('console').log(`Started deploy, PID: ${deployResPromise.pid}`)

deployResPromise.stdout.on('data', this.handleOutput.bind(this))
deployResPromise.stderr.on('data', this.handleOutput.bind(this))

deployResPromise.on('error', (err) => {
this._deployOutput += this.getTimestampPrefix() + `Error during deployment: ${err.message}\n`
require('console').error(`Error during deployment: ${err.message}`)
})

deployResPromise.on('spawn', (err) => {
this._deployOutput += this.getTimestampPrefix() + `Process spawned\n`
require('console').error(`Process spawned`)
})

deployResPromise.on('disconnect', (err) => {
this._deployOutput += this.getTimestampPrefix() + `Process disconnected\n`
require('console').error(`Process disconnected`)
})

deployResPromise.stdout.on('data', handleOutput)
deployResPromise.stderr.on('data', handleOutput)
deployResPromise.on('close', (code, signal) => {
this._deployOutput +=
this.getTimestampPrefix() + `Process closed with code: ${code} / signal: ${signal}\n`
require('console').error(`Process closed with code: ${code} / signal: ${signal}`)
})

deployResPromise.on('exit', (code, signal) => {
this._deployOutput +=
this.getTimestampPrefix() + `Process exited with code: ${code} / signal: ${signal}\n`
require('console').error(`Process exited with code: ${code} / signal: ${signal}`)
})

this._intervalsToClear.push(
setInterval(() => {
this._deployOutput +=
this.getTimestampPrefix() +
`Waiting for netlify deploy process to finish ... (killed: ${deployResPromise.killed}, connected: ${deployResPromise.connected})\n`
}, 5000),
)

this._intervalsToClear.push(
setInterval(() => {
this.ps(deployResPromise.pid)
}, 30_000),
)

deployResPromise
.then((result) => {
require('console').log(`Netlify deploy process finished.`)
this._deployOutput += this.getTimestampPrefix() + 'Netlify deploy process finished.\n'
})
.catch((err) => {
require('console').log(`Netlify deploy process failed. ` + err)
this._deployOutput += this.getTimestampPrefix() + 'Netlify deploy process failed. ' + err
})
.finally(() => {
require('console').log(`Netlify deploy process finally.`)
this._deployOutput += this.getTimestampPrefix() + 'Netlify deploy process finally.\n'
this.clearIntervals()
})

const deployRes = await deployResPromise

this.clearIntervals()

require('console').log(`Deploy finished. Processing output...`)

if (deployRes.exitCode !== 0) {
// clear deploy output to avoid printing it again in destroy()
this._deployOutput = ''
Expand Down Expand Up @@ -184,7 +305,7 @@ export class NextDeployInstance extends NextInstance {
}
} catch (err) {
require('console').error(err)
throw new Error(`Failed to parse deploy output: ${deployRes.stdout}`)
throw new Error(`Failed to parse deploy output: "${deployRes.stdout}"`)
}

this._buildId = (
Expand All @@ -195,12 +316,13 @@ export class NextDeployInstance extends NextInstance {
).trim()

require('console').log(`Got buildId: ${this._buildId}`)
require('console').log(`Setup time: ${(Date.now() - setupStartTime) / 1000.0}s`)
require('console').log(`Setup time: ${(Date.now() - this._setupStartTime) / 1000.0}s`)

this._isCurrentlyDeploying = false
}

public async destroy(): Promise<void> {
this.clearIntervals()
if (this._shouldDeleteDeploy) {
require('console').log(`Deleting project with deploy_id ${this._deployId}`)

Expand Down
Loading