Skip to content

Commit

Permalink
ci(getting-started): mitigate registry/yarn flake (#9799)
Browse files Browse the repository at this point in the history
closes: #9325, closes: #9710

## Description
This is a minor fix to make sure the `registry/yarn` getting-started CI test doesn't fail.  It embraces the current situation that `agoric install $DISTTAG` is not currently usable within the default [Agoric/dapp-offer-up](https://github.com/Agoric/dapp-offer-up) because of the long list of `packageJson.resolutions`  `dapp-offer-up` uses.

Thus, `getting-started.js` has `AGORIC_INSTALL_DISTTAG` set to `false`.  This ensures that `agoric install $DISTTAG` does not execute.  It is replaced by simply `yarn install`.
  • Loading branch information
mergify[bot] authored Aug 5, 2024
2 parents ddf71d7 + 110326f commit fc07a0f
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
# won't increase the time for this workflow to complete.
timeout_minutes: 20
command: scripts/registry.sh test ${{ matrix.cli }} ${{steps.get-branch.outputs.result}}
on_retry_command: rm $HOME/bin/agoric
on_retry_command: rm -f $HOME/bin/agoric

- name: notify on failure
if: >
Expand Down
1 change: 1 addition & 0 deletions packages/agoric-cli/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const makePspawn = ({
* @param {string | [string, string, string]} [param2.stdio] standard IO
* specification
* @param {Record<string, string | undefined>} [param2.env] environment
* @param {boolean} [param2.detached] whether the child process should be detached
* @returns {Promise<number> & { childProcess: ChildProcess }}} promise for
* exit status. The return result has a `childProcess` property to obtain
* control over the running process
Expand Down
107 changes: 92 additions & 15 deletions packages/agoric-cli/tools/getting-started.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* global process setTimeout setInterval clearInterval */
// @ts-check
/* global process setTimeout setInterval clearInterval Buffer */

import fs from 'fs';
import path from 'path';
Expand All @@ -10,31 +11,73 @@ import { spawn } from 'child_process';

import { makePspawn } from '../src/helpers.js';

const TIMEOUT_SECONDS = 3 * 60;
const RETRY_BLOCKHEIGHT_SECONDS = 3;
const SOCKET_TIMEOUT_SECONDS = 2;

// TODO: Set this to `true` when `agoric install $DISTTAG` properly updates the
// getting-started workflow's dependencies to current `@endo/*` and Agoric SDK
// from the local registry.
const AGORIC_INSTALL_DISTTAG = false;

const dirname = new URL('./', import.meta.url).pathname;

// To keep in sync with https://docs.agoric.com/guides/getting-started/

// Note that we currently only test:
// agoric init dapp-foo
// yarn install
// yarn install (or agoric install $DISTTAG)
// yarn start:docker
// yarn start:contract
// yarn start:ui

/**
* @param {string} url
* @returns {Promise<bigint>}
*/
const getLatestBlockHeight = url =>
new Promise((resolve, reject) => {
const req = request(url, res => {
if (!res) {
reject(Error('no result'));
return;
}
const bodyChunks = [];
res
.on('data', chunk => bodyChunks.push(chunk))
.on('end', () => {
const body = Buffer.concat(bodyChunks).toString('utf8');
const { statusCode = 0 } = res;
if (statusCode >= 200 && statusCode < 300) {
const { result: { sync_info: sync = {} } = {} } = JSON.parse(body);
if (sync.catching_up === false) {
resolve(BigInt(sync.latest_block_height));
return;
}
}
reject(Error(`Cannot get block height: ${statusCode} ${body}`));
});
});
req.setTimeout(SOCKET_TIMEOUT_SECONDS * 1_000);
req.on('error', reject);
req.end();
});

export const gettingStartedWorkflowTest = async (t, options = {}) => {
const { init: initOptions = [] } = options;
const { init: initOptions = [], install: installOptions = [] } = options;
const pspawn = makePspawn({ spawn });

// Kill an entire process group.
const pkill = (cp, signal = 'SIGINT') => process.kill(-cp.pid, signal);

/** @param {Parameters<typeof pspawn>} args */
function pspawnStdout(...args) {
const ps = pspawn(...args);
ps.childProcess.stdout.on('data', chunk => {
process.stdout.write(chunk);
});
const { stdout } = ps.childProcess;
if (stdout) {
stdout.on('data', chunk => {
process.stdout.write(chunk);
});
}
// ps.childProcess.unref();
return ps;
}
Expand Down Expand Up @@ -107,17 +150,51 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => {
);
process.chdir('dapp-foo');

// ==============
// yarn install
t.is(await yarn(['install']), 0, 'yarn install works');
if (AGORIC_INSTALL_DISTTAG && process.env.AGORIC_INSTALL_OPTIONS) {
// ==============
// agoric install $DISTTAG
const opts = JSON.parse(process.env.AGORIC_INSTALL_OPTIONS);
installOptions.push(...opts);
t.is(
await myMain(['install', ...installOptions]),
0,
'agoric install works',
);
} else {
// ==============
// yarn install
t.is(await yarn(['install', ...installOptions]), 0, 'yarn install works');
}

// ==============
// yarn start:docker
t.is(await yarn(['start:docker']), 0, 'yarn start:docker works');

// XXX: use abci_info endpoint to get block height
// sleep to let contract start
await new Promise(resolve => setTimeout(resolve, TIMEOUT_SECONDS));
// ==============
// wait for the chain to start
let lastKnownBlockHeight = 0n;
for (;;) {
try {
const currentHeight = await getLatestBlockHeight(
'http://localhost:26657/status',
);
if (currentHeight > lastKnownBlockHeight) {
const earlierHeight = lastKnownBlockHeight;
lastKnownBlockHeight = currentHeight;
if (earlierHeight > 0n && currentHeight > earlierHeight) {
// We've had at least one block produced.
break;
}
}
} catch (e) {
console.error((e && e.message) || e);
}

// Wait a bit and try again.
await new Promise(resolve =>
setTimeout(resolve, RETRY_BLOCKHEIGHT_SECONDS * 1_000),
);
}

// ==============
// yarn start:contract
Expand Down Expand Up @@ -145,9 +222,9 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => {
const req = request('http://localhost:5173/', _res => {
resolve('listening');
});
req.setTimeout(2000);
req.setTimeout(SOCKET_TIMEOUT_SECONDS * 1_000);
req.on('error', err => {
if (err.code !== 'ECONNREFUSED') {
if (!('code' in err) || err.code !== 'ECONNREFUSED') {
resolve(`Cannot connect to UI server: ${err}`);
}
});
Expand Down
2 changes: 1 addition & 1 deletion scripts/get-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ WORKDIR=${1:-.}
cd -- "$WORKDIR"
npm query .workspace \
| jq -r '.[].location | "\(.)/package.json"' \
| xargs jq '{key: .name, value: "^\(.version)"}' \
| xargs jq 'select(.private | not) | {key: .name, value: "^\(.version)"}' \
| jq --slurp from_entries
2 changes: 1 addition & 1 deletion scripts/registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ integrationTest() {
;;
*)
yarn global add "agoric@$DISTTAG"
persistVar AGORIC_CMD '["agoric"]'
persistVar AGORIC_CMD "[\"$(yarn global bin)/agoric\"]"
;;
esac

Expand Down
2 changes: 1 addition & 1 deletion scripts/resolve-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set -ueo pipefail

DIR=$(dirname -- "${BASH_SOURCE[0]}")

cd -- "$DIR/.."
cd -- "${1-$DIR/..}"

override=$(jq 'to_entries | map({ key: ("**/" + .key), value: .value }) | from_entries')

Expand Down

0 comments on commit fc07a0f

Please sign in to comment.