Example #1
0
    it('completes the current task before cancelling, and can be resumed', function() {
      let pipeline;

      class SometimesBuildCanceller extends plugins.Noop {
        build() {
          super.build();

          // cancel the first build, but allow the rest to proceed
          if (this.buildCount === 1 || this.buildCount === 3) {
            pipeline.cancel();
          }
        }
      }

      const stepA = new SometimesBuildCanceller();

      const stepB = new plugins.Noop([stepA]);
      const stepC = new plugins.Noop([stepB]);

      pipeline = new Builder(stepC);

      // build #1
      let build = pipeline.build();

      // once the build has begun:
      // 1. allow StepA to complete
      // 2. cancel the build
      // 3. wait for the build settle
      //   (stepB and stepC should not have run)

      return expect(
        promiseFinally(build, () => {
          expect(stepA.buildCount).to.eql(1, 'stepA.buildCount');
          expect(stepB.buildCount).to.eql(0, 'stepB.buildCount');
          expect(stepC.buildCount).to.eql(0, 'stepC.buildCount');
        })
      )
        .to.eventually.be.rejectedWith('Build Canceled')
        .then(() => {
          // build #2
          let build = pipeline.build();

          return build.then(() => {
            expect(stepA.buildCount).to.eql(2, 'stepA.buildCount');
            expect(stepB.buildCount).to.eql(1, 'stepB.buildCount');
            expect(stepC.buildCount).to.eql(1, 'stepC.buildCount');

            // build #3
            return expect(pipeline.build())
              .to.eventually.be.rejectedWith('Build Canceled')
              .then(() => {
                // build will cancel again during stepA (before the stepB) so
                // only stepA should have made progress
                expect(stepA.buildCount).to.eql(3, 'stepA.buildCount');
                expect(stepB.buildCount).to.eql(1, 'stepB.buildCount');
                expect(stepC.buildCount).to.eql(1, 'stepC.buildCount');
              });
          });
        });
    });
Example #2
0
 promise = promise.then(() => {
   let needsEndNode = false;
   // We use a nested .then/.catch so that the .catch can only catch errors
   // from this node, but not from previous nodes.
   return promiseFinally(
     Promise.resolve()
       .then(() => this._cancelationRequest.throwIfRequested())
       .then(() => {
         this.emit('beginNode', nw);
         needsEndNode = true;
       })
       .then(() => nw.build()),
     () => {
       if (needsEndNode) {
         this.emit('endNode', nw);
       }
     }
   )
     .then(() => this._cancelationRequest.throwIfRequested())
     .catch(err => {
       if (Cancelation.isCancelationError(err)) {
         throw err;
       } else {
         throw new BuildError(err, nw);
       }
     });
 });
Example #3
0
        function isPersistent(options) {
          const builder = new FixtureBuilder(new BuildOncePlugin(options));

          return promiseFinally(
            builder
              .build()
              .then(() => builder.build())
              .then(obj => obj['foo.txt'] === 'test'),
            () => builder.cleanup()
          );
        }
Example #4
0
    it('it cancels immediately if cancelled immediately after build', function() {
      const step = new plugins.Deferred();
      let pipeline = new Builder(step);
      step.resolve();
      let build = pipeline.build();
      pipeline.cancel();

      return promiseFinally(
        Promise.resolve(expect(build).to.eventually.be.rejectedWith('Build Canceled')),
        () => {
          expect(step.buildCount).to.eql(0);
        }
      );
    });
Example #5
0
  start() {
    if (this._started) {
      throw new Error('Watcher.prototype.start() must not be called more than once');
    }

    const promise = new Promise((resolve, reject) => {
      this.app = this._connect().use(middleware(this._watcher));

      this.http = http.createServer(this.app);
      this.http.listen(this._port, this._host);

      this.http.on('listening', () => {
        console.log(`Serving on ${this._url}\n`);
        resolve(this._watcher.start());
      });

      this.http.on('error', error => {
        if (error.code !== 'EADDRINUSE') {
          throw error;
        }

        let message = `Oh snap 😫. It appears a server is already running on ${this._url}\n`;
        message += `Are you perhaps already running serve in another terminal window?\n`;
        reject(new Error(message));
      });

      process.addListener('SIGINT', this._boundStop);
      process.addListener('SIGTERM', this._boundStop);

      this._watcher.on('buildSuccess', () => {
        this.emit('buildSuccess');
        messages.onBuildSuccess(this._builder);
      });

      this._watcher.on('buildFailure', () => {
        this.emit('buildFailure');
        messages.onBuildFailure();
      });
    });

    return promiseFinally(promise, () => this.stop());
  }
Example #6
0
function buildToFixture(node) {
  const fixtureBuilder = new FixtureBuilder(node);
  return promiseFinally(fixtureBuilder.build(), fixtureBuilder.cleanup.bind(fixtureBuilder));
}
Example #7
0
  // Trigger a (re)build.
  //
  // Returns a promise that resolves when the build has finished. If there is a
  // build error, the promise is rejected with a Builder.BuildError instance.
  // This method will never throw, and it will never be rejected with anything
  // other than a BuildError.
  build() {
    if (this._cancelationRequest) {
      return Promise.reject(new BuilderError('Cannot start a build if one is already running'));
    }

    let promise = Promise.resolve();

    this.buildId++;

    this.nodeWrappers.forEach(nw => {
      // We use `.forEach` instead of `for` to close nested functions over `nw`

      // Wipe all buildState objects at the beginning of the build
      nw.buildState = {};

      promise = promise.then(() => {
        let needsEndNode = false;
        // We use a nested .then/.catch so that the .catch can only catch errors
        // from this node, but not from previous nodes.
        return promiseFinally(
          Promise.resolve()
            .then(() => this._cancelationRequest.throwIfRequested())
            .then(() => {
              this.emit('beginNode', nw);
              needsEndNode = true;
            })
            .then(() => nw.build()),
          () => {
            if (needsEndNode) {
              this.emit('endNode', nw);
            }
          }
        )
          .then(() => this._cancelationRequest.throwIfRequested())
          .catch(err => {
            if (Cancelation.isCancelationError(err)) {
              throw err;
            } else {
              throw new BuildError(err, nw);
            }
          });
      });
    });

    this._cancelationRequest = new CancelationRequest(promise);

    return promiseFinally(
      promise
        .then(() => {
          return this.outputNodeWrapper;
        })
        .then(outputNodeWrapper => {
          this.buildHeimdallTree(outputNodeWrapper);
        }),
      () => {
        let buildsSkipped = this.nodeWrappers.filter(nw => nw.buildState.built === false).length;
        logger.debug(`Total nodes skipped: ${buildsSkipped} out of ${this.nodeWrappers.length}`);

        this._cancelationRequest = null;
      }
    );
  }
Example #8
0
 // Destructor-like method. Waits on current node to finish building, then cleans up temp directories
 cleanup() {
   return promiseFinally(this.cancel(), () => this.builderTmpDirCleanup());
 }