<commit_before>package rx

import (
	"fmt"

	"github.com/reactivego/rx/subscriber"
)

//jig:template Subscriber
//jig:support

// Subscriber is an alias for the subscriber.Subscriber interface type.
type Subscriber subscriber.Subscriber

//jig:template SubscribeOptions

// Subscription is an alias for the subscriber.Subscription interface type.
type Subscription subscriber.Subscription

// SubscribeOptions is a struct with options for Subscribe related methods.
type SubscribeOptions struct {
	// SubscribeOn is the scheduler to run the observable subscription on.
	SubscribeOn Scheduler
	// OnSubscribe is called right after the subscription is created and before
	// subscribing continues further.
	OnSubscribe func(subscription Subscription)
	// OnUnsubscribe is called by the subscription to notify the client that the
	// subscription has been canceled.
	OnUnsubscribe func()
}

// NewSubscriber will return a newly created subscriber. Before returning the
// subscription the OnSubscribe callback (if set) will already have been called.
func (options SubscribeOptions) NewSubscriber() Subscriber {
	subscription := subscriber.NewWithCallback(options.OnUnsubscribe)
	if options.OnSubscribe != nil {
		options.OnSubscribe(subscription)
	}
	return subscription
}

// SubscribeOptionSetter is the type of a function for setting SubscribeOptions.
type SubscribeOptionSetter func(options *SubscribeOptions)

// SubscribeOn takes the scheduler to run the observable subscription on and
// additional setters. It will first set the SubscribeOn option before
// calling the other setters provided as a parameter.
func SubscribeOn(subscribeOn Scheduler, setters ...SubscribeOptionSetter) SubscribeOptionSetter {
	return func(options *SubscribeOptions) {
		options.SubscribeOn = subscribeOn
		for _, setter := range setters {
			setter(options)
		}
	}
}

// OnSubscribe takes a callback to be called on subscription.
func OnSubscribe(callback func(Subscription)) SubscribeOptionSetter {
	return func(options *SubscribeOptions) { options.OnSubscribe = callback }
}

// OnUnsubscribe takes a callback to be called on subscription cancelation.
func OnUnsubscribe(callback func()) SubscribeOptionSetter {
	return func(options *SubscribeOptions) { options.OnUnsubscribe = callback }
}

// NewSubscribeOptions will create a new SubscribeOptions struct and then call
// the setter on it to recursively set all the options. It then returns a
// pointer to the created SubscribeOptions struct.
func NewSubscribeOptions(setter SubscribeOptionSetter) *SubscribeOptions {
	options := &SubscribeOptions{}
	setter(options)
	return options
}

//jig:template Observable<Foo> Subscribe
//jig:needs NewScheduler, SubscribeOptions

// Subscribe operates upon the emissions and notifications from an Observable.
// This method returns a Subscriber.
func (o ObservableFoo) Subscribe(observe FooObserveFunc, setters ...SubscribeOptionSetter) Subscriber {
	scheduler := NewTrampoline()
	setter := SubscribeOn(scheduler, setters...)
	options := NewSubscribeOptions(setter)
	subscriber := options.NewSubscriber()
	observer := func(next foo, err error, done bool) {
		if !done {
			observe(next, err, done)
		} else {
			observe(zeroFoo, err, true)
			subscriber.Unsubscribe()
		}
	}
	o(observer, options.SubscribeOn, subscriber)
	return subscriber
}

//jig:template Observable<Foo> SubscribeNext
//jig:needs Observable<Foo> Subscribe

// SubscribeNext operates upon the emissions from an Observable only.
// This method returns a Subscription.
func (o ObservableFoo) SubscribeNext(f func(next foo), setters ...SubscribeOptionSetter) Subscription {
	return o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			f(next)
		}
	}, setters...)
}

//jig:template Observable<Foo> Println
//jig:needs Observable<Foo> Subscribe

// Println subscribes to the Observable and prints every item to os.Stdout while
// it waits for completion or error. Returns either the error or nil when the
// Observable completed normally.
func (o ObservableFoo) Println(setters ...SubscribeOptionSetter) (e error) {
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			fmt.Println(next)
		} else {
			e = err
		}
	}, setters...).Wait()
	return e
}

//jig:template Observable ToChan
//jig:needs Observable Subscribe

// ToChan returns a channel that emits interface{} values. If the source
// observable does not emit values but emits an error or complete, then the
// returned channel will enit any error and then close without emitting any
// values.
//
// Because the channel is fed by subscribing to the observable, ToChan would
// block when subscribed on the standard Trampoline scheduler which is initially
// synchronous. That's why the subscribing is done on the Goroutine scheduler.
//
// To cancel the subscription created internally by ToChan you will need access
// to the subscription used internnally by ToChan. To get at this subscription,
// pass the result of a call to option OnSubscribe(func(Subscription)) as a
// parameter to ToChan. On suscription the callback will be called with the
// subscription that was created.
func (o Observable) ToChan(setters ...SubscribeOptionSetter) <-chan interface{} {
	scheduler := NewGoroutine()
	nextch := make(chan interface{}, 1)
	o.Subscribe(func(next interface{}, err error, done bool) {
		if !done {
			nextch <- next
		} else {
			if err != nil {
				nextch <- err
			}
			close(nextch)
		}
	}, SubscribeOn(scheduler, setters...))
	return nextch
}

//jig:template Observable<Foo> ToChan
//jig:needs Observable<Foo> Subscribe
//jig:required-vars Foo

// ToChan returns a channel that emits foo values. If the source observable does
// not emit values but emits an error or complete, then the returned channel
// will close without emitting any values.
//
// There is no way to determine whether the observable feeding into the
// channel terminated with an error or completed normally.
// Because the channel is fed by subscribing to the observable, ToChan would
// block when subscribed on the standard Trampoline scheduler which is initially
// synchronous. That's why the subscribing is done on the Goroutine scheduler.
// It is not possible to cancel the subscription created internally by ToChan.
func (o ObservableFoo) ToChan(setters ...SubscribeOptionSetter) <-chan foo {
	scheduler := NewGoroutine()
	nextch := make(chan foo, 1)
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			nextch <- next
		} else {
			close(nextch)
		}
	}, SubscribeOn(scheduler, setters...))
	return nextch
}

//jig:template Observable<Foo> ToSingle
//jig:needs Observable<Foo> Subscribe

// ToSingle blocks until the ObservableFoo emits exactly one value or an error.
// The value and any error are returned.
//
// This function subscribes to the source observable on the Goroutine scheduler.
// The Goroutine scheduler works in more situations for complex chains of
// observables, like when merging the output of multiple observables.
func (o ObservableFoo) ToSingle(setters ...SubscribeOptionSetter) (v foo, e error) {
	scheduler := NewGoroutine()
	o.Single().Subscribe(func(next foo, err error, done bool) {
		if !done {
			v = next
		} else {
			e = err
		}
	}, SubscribeOn(scheduler, setters...)).Wait()
	return v, e
}

//jig:template Observable<Foo> ToSlice
//jig:needs Observable<Foo> Subscribe

// ToSlice collects all values from the ObservableFoo into an slice. The
// complete slice and any error are returned.
//
// This function subscribes to the source observable on the Goroutine scheduler.
// The Goroutine scheduler works in more situations for complex chains of
// observables, like when merging the output of multiple observables.
func (o ObservableFoo) ToSlice(setters ...SubscribeOptionSetter) (a []foo, e error) {
	scheduler := NewGoroutine()
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			a = append(a, next)
		} else {
			e = err
		}
	}, SubscribeOn(scheduler, setters...)).Wait()
	return a, e
}

//jig:template Observable<Foo> Wait
//jig:needs Observable<Foo> Subscribe

// Wait subscribes to the Observable and waits for completion or error.
// Returns either the error or nil when the Observable completed normally.
func (o ObservableFoo) Wait(setters ...SubscribeOptionSetter) (e error) {
	o.Subscribe(func(next foo, err error, done bool) {
		if done {
			e = err
		}
	}, setters...).Wait()
	return e
}
<commit_msg>Rename var.<commit_after>package rx

import (
	"fmt"

	"github.com/reactivego/rx/subscriber"
)

//jig:template Subscriber
//jig:support

// Subscriber is an alias for the subscriber.Subscriber interface type.
type Subscriber subscriber.Subscriber

//jig:template SubscribeOptions

// Subscription is an alias for the subscriber.Subscription interface type.
type Subscription subscriber.Subscription

// SubscribeOptions is a struct with options for Subscribe related methods.
type SubscribeOptions struct {
	// SubscribeOn is the scheduler to run the observable subscription on.
	SubscribeOn Scheduler
	// OnSubscribe is called right after the subscription is created and before
	// subscribing continues further.
	OnSubscribe func(subscription Subscription)
	// OnUnsubscribe is called by the subscription to notify the client that the
	// subscription has been canceled.
	OnUnsubscribe func()
}

// NewSubscriber will return a newly created subscriber. Before returning the
// subscription the OnSubscribe callback (if set) will already have been called.
func (options SubscribeOptions) NewSubscriber() Subscriber {
	subscriber := subscriber.NewWithCallback(options.OnUnsubscribe)
	if options.OnSubscribe != nil {
		options.OnSubscribe(subscriber)
	}
	return subscriber
}

// SubscribeOptionSetter is the type of a function for setting SubscribeOptions.
type SubscribeOptionSetter func(options *SubscribeOptions)

// SubscribeOn takes the scheduler to run the observable subscription on and
// additional setters. It will first set the SubscribeOn option before
// calling the other setters provided as a parameter.
func SubscribeOn(subscribeOn Scheduler, setters ...SubscribeOptionSetter) SubscribeOptionSetter {
	return func(options *SubscribeOptions) {
		options.SubscribeOn = subscribeOn
		for _, setter := range setters {
			setter(options)
		}
	}
}

// OnSubscribe takes a callback to be called on subscription.
func OnSubscribe(callback func(Subscription)) SubscribeOptionSetter {
	return func(options *SubscribeOptions) { options.OnSubscribe = callback }
}

// OnUnsubscribe takes a callback to be called on subscription cancelation.
func OnUnsubscribe(callback func()) SubscribeOptionSetter {
	return func(options *SubscribeOptions) { options.OnUnsubscribe = callback }
}

// NewSubscribeOptions will create a new SubscribeOptions struct and then call
// the setter on it to recursively set all the options. It then returns a
// pointer to the created SubscribeOptions struct.
func NewSubscribeOptions(setter SubscribeOptionSetter) *SubscribeOptions {
	options := &SubscribeOptions{}
	setter(options)
	return options
}

//jig:template Observable<Foo> Subscribe
//jig:needs NewScheduler, SubscribeOptions

// Subscribe operates upon the emissions and notifications from an Observable.
// This method returns a Subscriber.
func (o ObservableFoo) Subscribe(observe FooObserveFunc, setters ...SubscribeOptionSetter) Subscriber {
	scheduler := NewTrampoline()
	setter := SubscribeOn(scheduler, setters...)
	options := NewSubscribeOptions(setter)
	subscriber := options.NewSubscriber()
	observer := func(next foo, err error, done bool) {
		if !done {
			observe(next, err, done)
		} else {
			observe(zeroFoo, err, true)
			subscriber.Unsubscribe()
		}
	}
	o(observer, options.SubscribeOn, subscriber)
	return subscriber
}

//jig:template Observable<Foo> SubscribeNext
//jig:needs Observable<Foo> Subscribe

// SubscribeNext operates upon the emissions from an Observable only.
// This method returns a Subscription.
func (o ObservableFoo) SubscribeNext(f func(next foo), setters ...SubscribeOptionSetter) Subscription {
	return o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			f(next)
		}
	}, setters...)
}

//jig:template Observable<Foo> Println
//jig:needs Observable<Foo> Subscribe

// Println subscribes to the Observable and prints every item to os.Stdout while
// it waits for completion or error. Returns either the error or nil when the
// Observable completed normally.
func (o ObservableFoo) Println(setters ...SubscribeOptionSetter) (e error) {
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			fmt.Println(next)
		} else {
			e = err
		}
	}, setters...).Wait()
	return e
}

//jig:template Observable ToChan
//jig:needs Observable Subscribe

// ToChan returns a channel that emits interface{} values. If the source
// observable does not emit values but emits an error or complete, then the
// returned channel will enit any error and then close without emitting any
// values.
//
// Because the channel is fed by subscribing to the observable, ToChan would
// block when subscribed on the standard Trampoline scheduler which is initially
// synchronous. That's why the subscribing is done on the Goroutine scheduler.
//
// To cancel the subscription created internally by ToChan you will need access
// to the subscription used internnally by ToChan. To get at this subscription,
// pass the result of a call to option OnSubscribe(func(Subscription)) as a
// parameter to ToChan. On suscription the callback will be called with the
// subscription that was created.
func (o Observable) ToChan(setters ...SubscribeOptionSetter) <-chan interface{} {
	scheduler := NewGoroutine()
	nextch := make(chan interface{}, 1)
	o.Subscribe(func(next interface{}, err error, done bool) {
		if !done {
			nextch <- next
		} else {
			if err != nil {
				nextch <- err
			}
			close(nextch)
		}
	}, SubscribeOn(scheduler, setters...))
	return nextch
}

//jig:template Observable<Foo> ToChan
//jig:needs Observable<Foo> Subscribe
//jig:required-vars Foo

// ToChan returns a channel that emits foo values. If the source observable does
// not emit values but emits an error or complete, then the returned channel
// will close without emitting any values.
//
// There is no way to determine whether the observable feeding into the
// channel terminated with an error or completed normally.
// Because the channel is fed by subscribing to the observable, ToChan would
// block when subscribed on the standard Trampoline scheduler which is initially
// synchronous. That's why the subscribing is done on the Goroutine scheduler.
// It is not possible to cancel the subscription created internally by ToChan.
func (o ObservableFoo) ToChan(setters ...SubscribeOptionSetter) <-chan foo {
	scheduler := NewGoroutine()
	nextch := make(chan foo, 1)
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			nextch <- next
		} else {
			close(nextch)
		}
	}, SubscribeOn(scheduler, setters...))
	return nextch
}

//jig:template Observable<Foo> ToSingle
//jig:needs Observable<Foo> Subscribe

// ToSingle blocks until the ObservableFoo emits exactly one value or an error.
// The value and any error are returned.
//
// This function subscribes to the source observable on the Goroutine scheduler.
// The Goroutine scheduler works in more situations for complex chains of
// observables, like when merging the output of multiple observables.
func (o ObservableFoo) ToSingle(setters ...SubscribeOptionSetter) (v foo, e error) {
	scheduler := NewGoroutine()
	o.Single().Subscribe(func(next foo, err error, done bool) {
		if !done {
			v = next
		} else {
			e = err
		}
	}, SubscribeOn(scheduler, setters...)).Wait()
	return v, e
}

//jig:template Observable<Foo> ToSlice
//jig:needs Observable<Foo> Subscribe

// ToSlice collects all values from the ObservableFoo into an slice. The
// complete slice and any error are returned.
//
// This function subscribes to the source observable on the Goroutine scheduler.
// The Goroutine scheduler works in more situations for complex chains of
// observables, like when merging the output of multiple observables.
func (o ObservableFoo) ToSlice(setters ...SubscribeOptionSetter) (a []foo, e error) {
	scheduler := NewGoroutine()
	o.Subscribe(func(next foo, err error, done bool) {
		if !done {
			a = append(a, next)
		} else {
			e = err
		}
	}, SubscribeOn(scheduler, setters...)).Wait()
	return a, e
}

//jig:template Observable<Foo> Wait
//jig:needs Observable<Foo> Subscribe

// Wait subscribes to the Observable and waits for completion or error.
// Returns either the error or nil when the Observable completed normally.
func (o ObservableFoo) Wait(setters ...SubscribeOptionSetter) (e error) {
	o.Subscribe(func(next foo, err error, done bool) {
		if done {
			e = err
		}
	}, setters...).Wait()
	return e
}
<|endoftext|>


<commit_before>package gothumb

import (
	"github.com/nfnt/resize"
	"image"
	_ "image/gif"
	"image/jpeg"
	_ "image/png"
	"os"
)

func GenericThumbnail(input string, output string, size int, quality int) (err error) {
	reader, err := os.Open(input)

	if err != nil {
		return err
	}

	defer reader.Close()

	writer, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0600)

	if err != nil {
		return err
	}

	defer writer.Close()

	img, _, err := image.Decode(reader)

	if err != nil {
		return err
	}

	var thumb image.Image

	if img.Bounds().Size().X >= img.Bounds().Size().Y {
		if img.Bounds().Size().X > size {
			thumb = resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
		} else {
			thumb = img
		}
	} else {
		if img.Bounds().Size().Y > size {
			thumb = resize.Resize(0, uint(size), img, resize.NearestNeighbor)
		} else {
			thumb = img
		}
	}

	opts := &jpeg.Options{
		Quality: quality,
	}

	jpeg.Encode(writer, thumb, opts)

	return
}
<commit_msg>move to forked resize<commit_after>package gothumb

import (
	"github.com/koofr/resize"
	"image"
	_ "image/gif"
	"image/jpeg"
	_ "image/png"
	"os"
)

func GenericThumbnail(input string, output string, size int, quality int) (err error) {
	reader, err := os.Open(input)

	if err != nil {
		return err
	}

	defer reader.Close()

	writer, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0600)

	if err != nil {
		return err
	}

	defer writer.Close()

	img, _, err := image.Decode(reader)

	if err != nil {
		return err
	}

	var thumb image.Image

	if img.Bounds().Size().X >= img.Bounds().Size().Y {
		if img.Bounds().Size().X > size {
			thumb = resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
		} else {
			thumb = img
		}
	} else {
		if img.Bounds().Size().Y > size {
			thumb = resize.Resize(0, uint(size), img, resize.NearestNeighbor)
		} else {
			thumb = img
		}
	}

	opts := &jpeg.Options{
		Quality: quality,
	}

	jpeg.Encode(writer, thumb, opts)

	return
}
<|endoftext|>


<commit_before>//@auther foolbread
//@time 2016-01-04
//@file aqua/singlechat_server/server/server.go
package server

import (
	"fbcommon/golog"
	fbtime "fbcommon/time"
	"time"
)

func init() {
	golog.Info("initing singlechat server ......")
	g_singlechat = newSinglechatServer()
	g_conmanager = newConnectManager()

	go logic_timer.Start()
}

var logic_timer *fbtime.Timer = fbtime.New(1 * time.Second)
var default_timeout time.Duration = 2 * time.Minute

var g_singlechat *singlechatServer
var g_conmanager *connectManager
<commit_msg>*修改server初始化方式<commit_after>//@auther foolbread
//@time 2016-01-04
//@file aqua/singlechat_server/server/server.go
package server

import (
	"fbcommon/golog"
	fbtime "fbcommon/time"
	"time"
)

func InitServer() {
	golog.Info("initing singlechat server ......")
	g_singlechat = newSinglechatServer()
	g_conmanager = newConnectManager()

	go logic_timer.Start()
}

var logic_timer *fbtime.Timer = fbtime.New(1 * time.Second)
var default_timeout time.Duration = 2 * time.Minute

var g_singlechat *singlechatServer
var g_conmanager *connectManager
<|endoftext|>


<commit_before>package cloud

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-tfe"
)

type taskResultSummary struct {
	unreachable     bool
	pending         int
	failed          int
	failedMandatory int
	passed          int
}

type taskStageReadFunc func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error)

func summarizeTaskResults(taskResults []*tfe.TaskResult) *taskResultSummary {
	var pendingCount, errCount, errMandatoryCount, passedCount int
	for _, task := range taskResults {
		if task.Status == "unreachable" {
			return &taskResultSummary{
				unreachable: true,
			}
		} else if task.Status == "running" || task.Status == "pendingCountnding" {
			pendingCount++
		} else if task.Status == "passed" {
			passedCount++
		} else {
			// Everything else is a failure
			errCount++
			if task.WorkspaceTaskEnforcementLevel == "mandatory" {
				errMandatoryCount++
			}
		}
	}

	return &taskResultSummary{
		unreachable:     false,
		pending:         pendingCount,
		failed:          errCount,
		failedMandatory: errMandatoryCount,
		passed:          passedCount,
	}
}

func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output IntegrationOutputWriter, fetchTaskStage taskStageReadFunc) error {
	return context.Poll(func(i int) (bool, error) {
		stage, err := fetchTaskStage(b, context.StopContext)

		if err != nil {
			return false, generalError("Failed to retrieve pre-apply task stage", err)
		}

		summary := summarizeTaskResults(stage.TaskResults)

		if summary.unreachable {
			output.Output("Skipping task results.")
			output.End()
			return false, nil
		}

		if summary.pending > 0 {
			pendingMessage := "%d tasks still pending, %d passed, %d failed ... "
			message := fmt.Sprintf(pendingMessage, summary.pending, summary.passed, summary.failed)

			if i%4 == 0 {
				if i > 0 {
					output.OutputElapsed(message, len(pendingMessage)) // Up to 2 digits are allowed by the max message allocation
				}
			}
			return true, nil
		}

		// No more tasks pending/running. Print all the results.

		// Track the first task name that is a mandatory enforcement level breach.
		var firstMandatoryTaskFailed *string = nil

		if i == 0 {
			output.Output(fmt.Sprintf("All tasks completed! %d passed, %d failed", summary.passed, summary.failed))
		} else {
			output.OutputElapsed(fmt.Sprintf("All tasks completed! %d passed, %d failed", summary.passed, summary.failed), 50)
		}

		output.Output("")

		for _, t := range stage.TaskResults {
			capitalizedStatus := string(t.Status)
			capitalizedStatus = strings.ToUpper(capitalizedStatus[:1]) + capitalizedStatus[1:]

			status := "[green]" + capitalizedStatus
			if t.Status != "passed" {
				level := string(t.WorkspaceTaskEnforcementLevel)
				level = strings.ToUpper(level[:1]) + level[1:]
				status = fmt.Sprintf("[red]%s (%s)", capitalizedStatus, level)

				if t.WorkspaceTaskEnforcementLevel == "mandatory" && firstMandatoryTaskFailed == nil {
					firstMandatoryTaskFailed = &t.TaskName
				}
			}

			title := fmt.Sprintf(`%s ⸺   %s`, t.TaskName, status)
			output.SubOutput(title)

			output.SubOutput(fmt.Sprintf("[dim]%s", t.Message))
			output.SubOutput("")
		}

		// If a mandatory enforcement level is breached, return an error.
		var taskErr error = nil
		var overall string = "[green]Passed"
		if firstMandatoryTaskFailed != nil {
			overall = "[red]Failed"
			if summary.failedMandatory > 1 {
				taskErr = fmt.Errorf("the run failed because %d mandatory tasks are required to succeed", summary.failedMandatory)
			} else {
				taskErr = fmt.Errorf("the run failed because the run task, %s, is required to succeed", *firstMandatoryTaskFailed)
			}
		} else if summary.failed > 0 { // we have failures but none of them mandatory
			overall = "[green]Passed with advisory failures"
		}

		output.SubOutput("")
		output.SubOutput("[bold]Overall Result: " + overall)

		output.End()

		return false, taskErr
	})
}

func (b *Cloud) runTasks(ctx *IntegrationContext, output IntegrationOutputWriter, stageID string) error {
	return b.runTasksWithTaskResults(ctx, output, func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error) {
		options := tfe.TaskStageReadOptions{
			Include: []tfe.TaskStageIncludeOpt{tfe.TaskStageTaskResults},
		}

		return b.client.TaskStages.Read(ctx.StopContext, stageID, &options)
	})
}
<commit_msg>fix typo for task.Status (#30978)<commit_after>package cloud

import (
	"context"
	"fmt"
	"strings"

	"github.com/hashicorp/go-tfe"
)

type taskResultSummary struct {
	unreachable     bool
	pending         int
	failed          int
	failedMandatory int
	passed          int
}

type taskStageReadFunc func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error)

func summarizeTaskResults(taskResults []*tfe.TaskResult) *taskResultSummary {
	var pendingCount, errCount, errMandatoryCount, passedCount int
	for _, task := range taskResults {
		if task.Status == "unreachable" {
			return &taskResultSummary{
				unreachable: true,
			}
		} else if task.Status == "running" || task.Status == "pending" {
			pendingCount++
		} else if task.Status == "passed" {
			passedCount++
		} else {
			// Everything else is a failure
			errCount++
			if task.WorkspaceTaskEnforcementLevel == "mandatory" {
				errMandatoryCount++
			}
		}
	}

	return &taskResultSummary{
		unreachable:     false,
		pending:         pendingCount,
		failed:          errCount,
		failedMandatory: errMandatoryCount,
		passed:          passedCount,
	}
}

func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output IntegrationOutputWriter, fetchTaskStage taskStageReadFunc) error {
	return context.Poll(func(i int) (bool, error) {
		stage, err := fetchTaskStage(b, context.StopContext)

		if err != nil {
			return false, generalError("Failed to retrieve pre-apply task stage", err)
		}

		summary := summarizeTaskResults(stage.TaskResults)

		if summary.unreachable {
			output.Output("Skipping task results.")
			output.End()
			return false, nil
		}

		if summary.pending > 0 {
			pendingMessage := "%d tasks still pending, %d passed, %d failed ... "
			message := fmt.Sprintf(pendingMessage, summary.pending, summary.passed, summary.failed)

			if i%4 == 0 {
				if i > 0 {
					output.OutputElapsed(message, len(pendingMessage)) // Up to 2 digits are allowed by the max message allocation
				}
			}
			return true, nil
		}

		// No more tasks pending/running. Print all the results.

		// Track the first task name that is a mandatory enforcement level breach.
		var firstMandatoryTaskFailed *string = nil

		if i == 0 {
			output.Output(fmt.Sprintf("All tasks completed! %d passed, %d failed", summary.passed, summary.failed))
		} else {
			output.OutputElapsed(fmt.Sprintf("All tasks completed! %d passed, %d failed", summary.passed, summary.failed), 50)
		}

		output.Output("")

		for _, t := range stage.TaskResults {
			capitalizedStatus := string(t.Status)
			capitalizedStatus = strings.ToUpper(capitalizedStatus[:1]) + capitalizedStatus[1:]

			status := "[green]" + capitalizedStatus
			if t.Status != "passed" {
				level := string(t.WorkspaceTaskEnforcementLevel)
				level = strings.ToUpper(level[:1]) + level[1:]
				status = fmt.Sprintf("[red]%s (%s)", capitalizedStatus, level)

				if t.WorkspaceTaskEnforcementLevel == "mandatory" && firstMandatoryTaskFailed == nil {
					firstMandatoryTaskFailed = &t.TaskName
				}
			}

			title := fmt.Sprintf(`%s ⸺   %s`, t.TaskName, status)
			output.SubOutput(title)

			output.SubOutput(fmt.Sprintf("[dim]%s", t.Message))
			output.SubOutput("")
		}

		// If a mandatory enforcement level is breached, return an error.
		var taskErr error = nil
		var overall string = "[green]Passed"
		if firstMandatoryTaskFailed != nil {
			overall = "[red]Failed"
			if summary.failedMandatory > 1 {
				taskErr = fmt.Errorf("the run failed because %d mandatory tasks are required to succeed", summary.failedMandatory)
			} else {
				taskErr = fmt.Errorf("the run failed because the run task, %s, is required to succeed", *firstMandatoryTaskFailed)
			}
		} else if summary.failed > 0 { // we have failures but none of them mandatory
			overall = "[green]Passed with advisory failures"
		}

		output.SubOutput("")
		output.SubOutput("[bold]Overall Result: " + overall)

		output.End()

		return false, taskErr
	})
}

func (b *Cloud) runTasks(ctx *IntegrationContext, output IntegrationOutputWriter, stageID string) error {
	return b.runTasksWithTaskResults(ctx, output, func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error) {
		options := tfe.TaskStageReadOptions{
			Include: []tfe.TaskStageIncludeOpt{tfe.TaskStageTaskResults},
		}

		return b.client.TaskStages.Read(ctx.StopContext, stageID, &options)
	})
}
<|endoftext|>


<commit_before>package selected

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/graph-gophers/graphql-go/errors"
	"github.com/graph-gophers/graphql-go/internal/common"
	"github.com/graph-gophers/graphql-go/internal/exec/packer"
	"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
	"github.com/graph-gophers/graphql-go/internal/query"
	"github.com/graph-gophers/graphql-go/internal/schema"
	"github.com/graph-gophers/graphql-go/introspection"
)

type Request struct {
	Schema *schema.Schema
	Doc    *query.Document
	Vars   map[string]interface{}
	Mu     sync.Mutex
	Errs   []*errors.QueryError
}

func (r *Request) AddError(err *errors.QueryError) {
	r.Mu.Lock()
	r.Errs = append(r.Errs, err)
	r.Mu.Unlock()
}

func ApplyOperation(r *Request, s *resolvable.Schema, op *query.Operation) []Selection {
	var obj *resolvable.Object
	switch op.Type {
	case query.Query:
		obj = s.Query.(*resolvable.Object)
	case query.Mutation:
		obj = s.Mutation.(*resolvable.Object)
	case query.Subscription:
		obj = s.Subscription.(*resolvable.Object)
	}
	return applySelectionSet(r, obj, op.Selections)
}

type Selection interface {
	isSelection()
}

type SchemaField struct {
	resolvable.Field
	Alias       string
	Args        map[string]interface{}
	PackedArgs  reflect.Value
	Sels        []Selection
	Async       bool
	FixedResult reflect.Value
}

type TypeAssertion struct {
	resolvable.TypeAssertion
	Sels []Selection
}

type TypenameField struct {
	resolvable.Object
	Alias string
}

func (*SchemaField) isSelection()   {}
func (*TypeAssertion) isSelection() {}
func (*TypenameField) isSelection() {}

func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection) (flattenedSels []Selection) {
	for _, sel := range sels {
		switch sel := sel.(type) {
		case *query.Field:
			field := sel
			if skipByDirective(r, field.Directives) {
				continue
			}

			switch field.Name.Name {
			case "__typename":
				flattenedSels = append(flattenedSels, &TypenameField{
					Object: *e,
					Alias:  field.Alias.Name,
				})

			case "__schema":
				flattenedSels = append(flattenedSels, &SchemaField{
					Field:       resolvable.MetaFieldSchema,
					Alias:       field.Alias.Name,
					Sels:        applySelectionSet(r, resolvable.MetaSchema, field.Selections),
					Async:       true,
					FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
				})

			case "__type":
				p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
				v, err := p.Pack(field.Arguments.MustGet("name").Value(r.Vars))
				if err != nil {
					r.AddError(errors.Errorf("%s", err))
					return nil
				}

				t, ok := r.Schema.Types[v.String()]
				if !ok {
					return nil
				}

				flattenedSels = append(flattenedSels, &SchemaField{
					Field:       resolvable.MetaFieldType,
					Alias:       field.Alias.Name,
					Sels:        applySelectionSet(r, resolvable.MetaType, field.Selections),
					Async:       true,
					FixedResult: reflect.ValueOf(introspection.WrapType(t)),
				})

			default:
				fe := e.Fields[field.Name.Name]

				var args map[string]interface{}
				var packedArgs reflect.Value
				if fe.ArgsPacker != nil {
					args = make(map[string]interface{})
					for _, arg := range field.Arguments {
						args[arg.Name.Name] = arg.Value.Value(r.Vars)
					}
					var err error
					packedArgs, err = fe.ArgsPacker.Pack(args)
					if err != nil {
						r.AddError(errors.Errorf("%s", err))
						return
					}
				}

				fieldSels := applyField(r, fe.ValueExec, field.Selections)
				flattenedSels = append(flattenedSels, &SchemaField{
					Field:      *fe,
					Alias:      field.Alias.Name,
					Args:       args,
					PackedArgs: packedArgs,
					Sels:       fieldSels,
					Async:      fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels),
				})
			}

		case *query.InlineFragment:
			frag := sel
			if skipByDirective(r, frag.Directives) {
				continue
			}
			flattenedSels = append(flattenedSels, applyFragment(r, e, &frag.Fragment)...)

		case *query.FragmentSpread:
			spread := sel
			if skipByDirective(r, spread.Directives) {
				continue
			}
			flattenedSels = append(flattenedSels, applyFragment(r, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)

		default:
			panic("invalid type")
		}
	}
	return
}

func applyFragment(r *Request, e *resolvable.Object, frag *query.Fragment) []Selection {
	if frag.On.Name != "" && frag.On.Name != e.Name {
		a, ok := e.TypeAssertions[frag.On.Name]
		if !ok {
			panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling
		}

		return []Selection{&TypeAssertion{
			TypeAssertion: *a,
			Sels:          applySelectionSet(r, a.TypeExec.(*resolvable.Object), frag.Selections),
		}}
	}
	return applySelectionSet(r, e, frag.Selections)
}

func applyField(r *Request, e resolvable.Resolvable, sels []query.Selection) []Selection {
	switch e := e.(type) {
	case *resolvable.Object:
		return applySelectionSet(r, e, sels)
	case *resolvable.List:
		return applyField(r, e.Elem, sels)
	case *resolvable.Scalar:
		return nil
	default:
		panic("unreachable")
	}
}

func skipByDirective(r *Request, directives common.DirectiveList) bool {
	if d := directives.Get("skip"); d != nil {
		p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
		v, err := p.Pack(d.Args.MustGet("if").Value(r.Vars))
		if err != nil {
			r.AddError(errors.Errorf("%s", err))
		}
		if err == nil && v.Bool() {
			return true
		}
	}

	if d := directives.Get("include"); d != nil {
		p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
		v, err := p.Pack(d.Args.MustGet("if").Value(r.Vars))
		if err != nil {
			r.AddError(errors.Errorf("%s", err))
		}
		if err == nil && !v.Bool() {
			return true
		}
	}

	return false
}

func HasAsyncSel(sels []Selection) bool {
	for _, sel := range sels {
		switch sel := sel.(type) {
		case *SchemaField:
			if sel.Async {
				return true
			}
		case *TypeAssertion:
			if HasAsyncSel(sel.Sels) {
				return true
			}
		case *TypenameField:
			// sync
		default:
			panic("unreachable")
		}
	}
	return false
}
<commit_msg>add SubPathHasError util on Request<commit_after>package selected

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/graph-gophers/graphql-go/errors"
	"github.com/graph-gophers/graphql-go/internal/common"
	"github.com/graph-gophers/graphql-go/internal/exec/packer"
	"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
	"github.com/graph-gophers/graphql-go/internal/query"
	"github.com/graph-gophers/graphql-go/internal/schema"
	"github.com/graph-gophers/graphql-go/introspection"
)

type Request struct {
	Schema *schema.Schema
	Doc    *query.Document
	Vars   map[string]interface{}
	Mu     sync.Mutex
	Errs   []*errors.QueryError
}

func (r *Request) AddError(err *errors.QueryError) {
	r.Mu.Lock()
	r.Errs = append(r.Errs, err)
	r.Mu.Unlock()
}

// pathPrefixMatch checks if `path` starts with `prefix`
func pathPrefixMatch(path []interface{}, prefix []interface{}) bool {
	if len(prefix) > len(path) {
		return false
	}
	for i, component := range prefix {
		if path[i] != component {
			return false
		}
	}
	return true
}

// SubPathHasError returns true if any path that is a subpath of the given path has added errors to the response
func (r *Request) SubPathHasError(path []interface{}) bool {
	r.Mu.Lock()
	defer r.Mu.Unlock()
	for _, err := range r.Errs {
		if pathPrefixMatch(err.Path, path) {
			return true
		}
	}
	return false
}

func ApplyOperation(r *Request, s *resolvable.Schema, op *query.Operation) []Selection {
	var obj *resolvable.Object
	switch op.Type {
	case query.Query:
		obj = s.Query.(*resolvable.Object)
	case query.Mutation:
		obj = s.Mutation.(*resolvable.Object)
	case query.Subscription:
		obj = s.Subscription.(*resolvable.Object)
	}
	return applySelectionSet(r, obj, op.Selections)
}

type Selection interface {
	isSelection()
}

type SchemaField struct {
	resolvable.Field
	Alias       string
	Args        map[string]interface{}
	PackedArgs  reflect.Value
	Sels        []Selection
	Async       bool
	FixedResult reflect.Value
}

type TypeAssertion struct {
	resolvable.TypeAssertion
	Sels []Selection
}

type TypenameField struct {
	resolvable.Object
	Alias string
}

func (*SchemaField) isSelection()   {}
func (*TypeAssertion) isSelection() {}
func (*TypenameField) isSelection() {}

func applySelectionSet(r *Request, e *resolvable.Object, sels []query.Selection) (flattenedSels []Selection) {
	for _, sel := range sels {
		switch sel := sel.(type) {
		case *query.Field:
			field := sel
			if skipByDirective(r, field.Directives) {
				continue
			}

			switch field.Name.Name {
			case "__typename":
				flattenedSels = append(flattenedSels, &TypenameField{
					Object: *e,
					Alias:  field.Alias.Name,
				})

			case "__schema":
				flattenedSels = append(flattenedSels, &SchemaField{
					Field:       resolvable.MetaFieldSchema,
					Alias:       field.Alias.Name,
					Sels:        applySelectionSet(r, resolvable.MetaSchema, field.Selections),
					Async:       true,
					FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
				})

			case "__type":
				p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
				v, err := p.Pack(field.Arguments.MustGet("name").Value(r.Vars))
				if err != nil {
					r.AddError(errors.Errorf("%s", err))
					return nil
				}

				t, ok := r.Schema.Types[v.String()]
				if !ok {
					return nil
				}

				flattenedSels = append(flattenedSels, &SchemaField{
					Field:       resolvable.MetaFieldType,
					Alias:       field.Alias.Name,
					Sels:        applySelectionSet(r, resolvable.MetaType, field.Selections),
					Async:       true,
					FixedResult: reflect.ValueOf(introspection.WrapType(t)),
				})

			default:
				fe := e.Fields[field.Name.Name]

				var args map[string]interface{}
				var packedArgs reflect.Value
				if fe.ArgsPacker != nil {
					args = make(map[string]interface{})
					for _, arg := range field.Arguments {
						args[arg.Name.Name] = arg.Value.Value(r.Vars)
					}
					var err error
					packedArgs, err = fe.ArgsPacker.Pack(args)
					if err != nil {
						r.AddError(errors.Errorf("%s", err))
						return
					}
				}

				fieldSels := applyField(r, fe.ValueExec, field.Selections)
				flattenedSels = append(flattenedSels, &SchemaField{
					Field:      *fe,
					Alias:      field.Alias.Name,
					Args:       args,
					PackedArgs: packedArgs,
					Sels:       fieldSels,
					Async:      fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels),
				})
			}

		case *query.InlineFragment:
			frag := sel
			if skipByDirective(r, frag.Directives) {
				continue
			}
			flattenedSels = append(flattenedSels, applyFragment(r, e, &frag.Fragment)...)

		case *query.FragmentSpread:
			spread := sel
			if skipByDirective(r, spread.Directives) {
				continue
			}
			flattenedSels = append(flattenedSels, applyFragment(r, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)

		default:
			panic("invalid type")
		}
	}
	return
}

func applyFragment(r *Request, e *resolvable.Object, frag *query.Fragment) []Selection {
	if frag.On.Name != "" && frag.On.Name != e.Name {
		a, ok := e.TypeAssertions[frag.On.Name]
		if !ok {
			panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling
		}

		return []Selection{&TypeAssertion{
			TypeAssertion: *a,
			Sels:          applySelectionSet(r, a.TypeExec.(*resolvable.Object), frag.Selections),
		}}
	}
	return applySelectionSet(r, e, frag.Selections)
}

func applyField(r *Request, e resolvable.Resolvable, sels []query.Selection) []Selection {
	switch e := e.(type) {
	case *resolvable.Object:
		return applySelectionSet(r, e, sels)
	case *resolvable.List:
		return applyField(r, e.Elem, sels)
	case *resolvable.Scalar:
		return nil
	default:
		panic("unreachable")
	}
}

func skipByDirective(r *Request, directives common.DirectiveList) bool {
	if d := directives.Get("skip"); d != nil {
		p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
		v, err := p.Pack(d.Args.MustGet("if").Value(r.Vars))
		if err != nil {
			r.AddError(errors.Errorf("%s", err))
		}
		if err == nil && v.Bool() {
			return true
		}
	}

	if d := directives.Get("include"); d != nil {
		p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
		v, err := p.Pack(d.Args.MustGet("if").Value(r.Vars))
		if err != nil {
			r.AddError(errors.Errorf("%s", err))
		}
		if err == nil && !v.Bool() {
			return true
		}
	}

	return false
}

func HasAsyncSel(sels []Selection) bool {
	for _, sel := range sels {
		switch sel := sel.(type) {
		case *SchemaField:
			if sel.Async {
				return true
			}
		case *TypeAssertion:
			if HasAsyncSel(sel.Sels) {
				return true
			}
		case *TypenameField:
			// sync
		default:
			panic("unreachable")
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package source

import (
	"bytes"
	"context"
	"fmt"
	"strings"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/asmdecl"
	"golang.org/x/tools/go/analysis/passes/assign"
	"golang.org/x/tools/go/analysis/passes/atomic"
	"golang.org/x/tools/go/analysis/passes/atomicalign"
	"golang.org/x/tools/go/analysis/passes/bools"
	"golang.org/x/tools/go/analysis/passes/buildtag"
	"golang.org/x/tools/go/analysis/passes/cgocall"
	"golang.org/x/tools/go/analysis/passes/composite"
	"golang.org/x/tools/go/analysis/passes/copylock"
	"golang.org/x/tools/go/analysis/passes/httpresponse"
	"golang.org/x/tools/go/analysis/passes/loopclosure"
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/passes/nilfunc"
	"golang.org/x/tools/go/analysis/passes/printf"
	"golang.org/x/tools/go/analysis/passes/shift"
	"golang.org/x/tools/go/analysis/passes/stdmethods"
	"golang.org/x/tools/go/analysis/passes/structtag"
	"golang.org/x/tools/go/analysis/passes/tests"
	"golang.org/x/tools/go/analysis/passes/unmarshal"
	"golang.org/x/tools/go/analysis/passes/unreachable"
	"golang.org/x/tools/go/analysis/passes/unsafeptr"
	"golang.org/x/tools/go/analysis/passes/unusedresult"
	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/log"
	"golang.org/x/tools/internal/telemetry/trace"
	errors "golang.org/x/xerrors"
)

type Diagnostic struct {
	URI      span.URI
	Range    protocol.Range
	Message  string
	Source   string
	Severity DiagnosticSeverity

	SuggestedFixes []SuggestedFix
}

type SuggestedFix struct {
	Title string
	Edits []protocol.TextEdit
}

type DiagnosticSeverity int

const (
	SeverityWarning DiagnosticSeverity = iota
	SeverityError
)

func Diagnostics(ctx context.Context, view View, f GoFile, disabledAnalyses map[string]struct{}) (map[span.URI][]Diagnostic, string, error) {
	ctx, done := trace.StartSpan(ctx, "source.Diagnostics", telemetry.File.Of(f.URI()))
	defer done()

	cphs, err := f.CheckPackageHandles(ctx)
	if err != nil {
		return nil, "", err
	}
	cph := NarrowestCheckPackageHandle(cphs)
	pkg, err := cph.Check(ctx)
	if err != nil {
		log.Error(ctx, "no package for file", err)
		return singleDiagnostic(f.URI(), "%s is not part of a package", f.URI()), "", nil
	}

	// Prepare the reports we will send for the files in this package.
	reports := make(map[span.URI][]Diagnostic)
	for _, fh := range pkg.Files() {
		clearReports(view, reports, fh.File().Identity().URI)
	}

	// If we have `go list` errors, we may want to offer a warning message to the user.
	var warningMsg string
	if hasListErrors(pkg.GetErrors()) {
		warningMsg, err = checkCommonErrors(ctx, view, f.URI())
		if err != nil {
			log.Error(ctx, "error checking common errors", err, telemetry.File.Of(f.URI))
		}
	}

	// Prepare any additional reports for the errors in this package.
	for _, err := range pkg.GetErrors() {
		if err.Kind != packages.ListError {
			continue
		}
		clearReports(view, reports, packagesErrorSpan(err).URI())
	}

	// Run diagnostics for the package that this URI belongs to.
	if !diagnostics(ctx, view, pkg, reports) {
		// If we don't have any list, parse, or type errors, run analyses.
		if err := analyses(ctx, view, cph, disabledAnalyses, reports); err != nil {
			log.Error(ctx, "failed to run analyses", err, telemetry.File.Of(f.URI()))
		}
	}
	// Updates to the diagnostics for this package may need to be propagated.
	revDeps := f.GetActiveReverseDeps(ctx)
	for _, f := range revDeps {
		cphs, err := f.CheckPackageHandles(ctx)
		if err != nil {
			return nil, "", err
		}
		cph := WidestCheckPackageHandle(cphs)
		pkg, err := cph.Check(ctx)
		if err != nil {
			return nil, warningMsg, err
		}
		for _, fh := range pkg.Files() {
			clearReports(view, reports, fh.File().Identity().URI)
		}
		diagnostics(ctx, view, pkg, reports)
	}
	return reports, warningMsg, nil
}

type diagnosticSet struct {
	listErrors, parseErrors, typeErrors []*Diagnostic
}

func diagnostics(ctx context.Context, view View, pkg Package, reports map[span.URI][]Diagnostic) bool {
	ctx, done := trace.StartSpan(ctx, "source.diagnostics", telemetry.Package.Of(pkg.ID()))
	defer done()

	diagSets := make(map[span.URI]*diagnosticSet)
	for _, err := range pkg.GetErrors() {
		spn := packagesErrorSpan(err)
		diag := &Diagnostic{
			URI:      spn.URI(),
			Message:  err.Msg,
			Source:   "LSP",
			Severity: SeverityError,
		}
		set, ok := diagSets[diag.URI]
		if !ok {
			set = &diagnosticSet{}
			diagSets[diag.URI] = set
		}
		switch err.Kind {
		case packages.ParseError:
			set.parseErrors = append(set.parseErrors, diag)
		case packages.TypeError:
			set.typeErrors = append(set.typeErrors, diag)
		default:
			set.listErrors = append(set.listErrors, diag)
		}
		rng, err := spanToRange(ctx, view, pkg, spn, err.Kind == packages.TypeError)
		if err != nil {
			log.Error(ctx, "failed to convert span to range", err)
			continue
		}
		diag.Range = rng
	}
	var nonEmptyDiagnostics bool // track if we actually send non-empty diagnostics
	for uri, set := range diagSets {
		// Don't report type errors if there are parse errors or list errors.
		diags := set.typeErrors
		if len(set.parseErrors) > 0 {
			diags = set.parseErrors
		} else if len(set.listErrors) > 0 {
			diags = set.listErrors
		}
		if len(diags) > 0 {
			nonEmptyDiagnostics = true
		}
		for _, diag := range diags {
			if _, ok := reports[uri]; ok {
				reports[uri] = append(reports[uri], *diag)
			}
		}
	}
	return nonEmptyDiagnostics
}

// spanToRange converts a span.Span to a protocol.Range,
// assuming that the span belongs to the package whose diagnostics are being computed.
func spanToRange(ctx context.Context, view View, pkg Package, spn span.Span, isTypeError bool) (protocol.Range, error) {
	ph, err := pkg.File(spn.URI())
	if err != nil {
		return protocol.Range{}, err
	}
	_, m, _, err := ph.Cached(ctx)
	if err != nil {
		return protocol.Range{}, err
	}
	data, _, err := ph.File().Read(ctx)
	if err != nil {
		return protocol.Range{}, err
	}
	// Try to get a range for the diagnostic.
	// TODO: Don't just limit ranges to type errors.
	if spn.IsPoint() && isTypeError {
		if s, err := spn.WithOffset(m.Converter); err == nil {
			start := s.Start()
			offset := start.Offset()
			if width := bytes.IndexAny(data[offset:], " \n,():;[]"); width > 0 {
				spn = span.New(spn.URI(), start, span.NewPoint(start.Line(), start.Column()+width, offset+width))
			}
		}
	}
	return m.Range(spn)
}

func analyses(ctx context.Context, view View, cph CheckPackageHandle, disabledAnalyses map[string]struct{}, reports map[span.URI][]Diagnostic) error {
	// Type checking and parsing succeeded. Run analyses.
	if err := runAnalyses(ctx, view, cph, disabledAnalyses, func(a *analysis.Analyzer, diag analysis.Diagnostic) error {
		diagnostic, err := toDiagnostic(ctx, view, diag, a.Name)
		if err != nil {
			return err
		}
		addReport(view, reports, diagnostic.URI, diagnostic)
		return nil
	}); err != nil {
		return err
	}
	return nil
}

func toDiagnostic(ctx context.Context, view View, diag analysis.Diagnostic, category string) (Diagnostic, error) {
	r := span.NewRange(view.Session().Cache().FileSet(), diag.Pos, diag.End)
	spn, err := r.Span()
	if err != nil {
		// The diagnostic has an invalid position, so we don't have a valid span.
		return Diagnostic{}, err
	}
	if diag.Category != "" {
		category += "." + category
	}
	f, err := view.GetFile(ctx, spn.URI())
	if err != nil {
		return Diagnostic{}, err
	}
	gof, ok := f.(GoFile)
	if !ok {
		return Diagnostic{}, errors.Errorf("%s is not a Go file", f.URI())
	}
	// If the package has changed since these diagnostics were computed,
	// this may be incorrect. Should the package be associated with the diagnostic?
	cphs, err := gof.CheckPackageHandles(ctx)
	if err != nil {
		return Diagnostic{}, err
	}
	cph := NarrowestCheckPackageHandle(cphs)
	pkg, err := cph.Cached(ctx)
	if err != nil {
		return Diagnostic{}, err
	}
	ca, err := getCodeActions(ctx, view, pkg, diag)
	if err != nil {
		return Diagnostic{}, err
	}

	rng, err := spanToRange(ctx, view, pkg, spn, false)
	if err != nil {
		return Diagnostic{}, err
	}
	return Diagnostic{
		URI:            spn.URI(),
		Range:          rng,
		Source:         category,
		Message:        diag.Message,
		Severity:       SeverityWarning,
		SuggestedFixes: ca,
	}, nil
}

func clearReports(v View, reports map[span.URI][]Diagnostic, uri span.URI) {
	if v.Ignore(uri) {
		return
	}
	reports[uri] = []Diagnostic{}
}

func addReport(v View, reports map[span.URI][]Diagnostic, uri span.URI, diagnostic Diagnostic) {
	if v.Ignore(uri) {
		return
	}
	if _, ok := reports[uri]; ok {
		reports[uri] = append(reports[uri], diagnostic)
	}
}

func packagesErrorSpan(err packages.Error) span.Span {
	if err.Pos == "" {
		return parseDiagnosticMessage(err.Msg)
	}
	return span.Parse(err.Pos)
}

// parseDiagnosticMessage attempts to parse a standard `go list` error message
// by stripping off the trailing error message.
//
// It works only on errors whose message is prefixed by colon,
// followed by a space (": "). For example:
//
//   attributes.go:13:1: expected 'package', found 'type'
//
func parseDiagnosticMessage(input string) span.Span {
	input = strings.TrimSpace(input)
	msgIndex := strings.Index(input, ": ")
	if msgIndex < 0 {
		return span.Parse(input)
	}
	return span.Parse(input[:msgIndex])
}

func singleDiagnostic(uri span.URI, format string, a ...interface{}) map[span.URI][]Diagnostic {
	return map[span.URI][]Diagnostic{
		uri: []Diagnostic{{
			Source:   "LSP",
			URI:      uri,
			Range:    protocol.Range{},
			Message:  fmt.Sprintf(format, a...),
			Severity: SeverityError,
		}},
	}
}

var Analyzers = []*analysis.Analyzer{
	// The traditional vet suite:
	asmdecl.Analyzer,
	assign.Analyzer,
	atomic.Analyzer,
	atomicalign.Analyzer,
	bools.Analyzer,
	buildtag.Analyzer,
	cgocall.Analyzer,
	composite.Analyzer,
	copylock.Analyzer,
	httpresponse.Analyzer,
	loopclosure.Analyzer,
	lostcancel.Analyzer,
	nilfunc.Analyzer,
	printf.Analyzer,
	shift.Analyzer,
	stdmethods.Analyzer,
	structtag.Analyzer,
	tests.Analyzer,
	unmarshal.Analyzer,
	unreachable.Analyzer,
	unsafeptr.Analyzer,
	unusedresult.Analyzer,
}

func runAnalyses(ctx context.Context, view View, cph CheckPackageHandle, disabledAnalyses map[string]struct{}, report func(a *analysis.Analyzer, diag analysis.Diagnostic) error) error {
	var analyzers []*analysis.Analyzer
	for _, a := range Analyzers {
		if _, ok := disabledAnalyses[a.Name]; ok {
			continue
		}
		analyzers = append(analyzers, a)
	}

	roots, err := analyze(ctx, view, []CheckPackageHandle{cph}, analyzers)
	if err != nil {
		return err
	}

	// Report diagnostics and errors from root analyzers.
	for _, r := range roots {
		var sdiags []Diagnostic
		for _, diag := range r.diagnostics {
			if r.err != nil {
				// TODO(matloob): This isn't quite right: we might return a failed prerequisites error,
				// which isn't super useful...
				return r.err
			}
			if err := report(r.Analyzer, diag); err != nil {
				return err
			}
			sdiag, err := toDiagnostic(ctx, view, diag, r.Analyzer.Name)
			if err != nil {
				return err
			}
			sdiags = append(sdiags, sdiag)
		}
		pkg, err := cph.Check(ctx)
		if err != nil {
			return err
		}
		pkg.SetDiagnostics(r.Analyzer, sdiags)
	}
	return nil
}
<commit_msg>internal/lsp: fix diagnostics to report for all available files<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package source

import (
	"bytes"
	"context"
	"fmt"
	"strings"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/asmdecl"
	"golang.org/x/tools/go/analysis/passes/assign"
	"golang.org/x/tools/go/analysis/passes/atomic"
	"golang.org/x/tools/go/analysis/passes/atomicalign"
	"golang.org/x/tools/go/analysis/passes/bools"
	"golang.org/x/tools/go/analysis/passes/buildtag"
	"golang.org/x/tools/go/analysis/passes/cgocall"
	"golang.org/x/tools/go/analysis/passes/composite"
	"golang.org/x/tools/go/analysis/passes/copylock"
	"golang.org/x/tools/go/analysis/passes/httpresponse"
	"golang.org/x/tools/go/analysis/passes/loopclosure"
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/passes/nilfunc"
	"golang.org/x/tools/go/analysis/passes/printf"
	"golang.org/x/tools/go/analysis/passes/shift"
	"golang.org/x/tools/go/analysis/passes/stdmethods"
	"golang.org/x/tools/go/analysis/passes/structtag"
	"golang.org/x/tools/go/analysis/passes/tests"
	"golang.org/x/tools/go/analysis/passes/unmarshal"
	"golang.org/x/tools/go/analysis/passes/unreachable"
	"golang.org/x/tools/go/analysis/passes/unsafeptr"
	"golang.org/x/tools/go/analysis/passes/unusedresult"
	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/log"
	"golang.org/x/tools/internal/telemetry/trace"
	errors "golang.org/x/xerrors"
)

type Diagnostic struct {
	URI      span.URI
	Range    protocol.Range
	Message  string
	Source   string
	Severity DiagnosticSeverity

	SuggestedFixes []SuggestedFix
}

type SuggestedFix struct {
	Title string
	Edits []protocol.TextEdit
}

type DiagnosticSeverity int

const (
	SeverityWarning DiagnosticSeverity = iota
	SeverityError
)

func Diagnostics(ctx context.Context, view View, f GoFile, disabledAnalyses map[string]struct{}) (map[span.URI][]Diagnostic, string, error) {
	ctx, done := trace.StartSpan(ctx, "source.Diagnostics", telemetry.File.Of(f.URI()))
	defer done()

	cphs, err := f.CheckPackageHandles(ctx)
	if err != nil {
		return nil, "", err
	}
	cph := WidestCheckPackageHandle(cphs)
	pkg, err := cph.Check(ctx)
	if err != nil {
		log.Error(ctx, "no package for file", err)
		return singleDiagnostic(f.URI(), "%s is not part of a package", f.URI()), "", nil
	}

	// Prepare the reports we will send for the files in this package.
	reports := make(map[span.URI][]Diagnostic)
	for _, fh := range pkg.Files() {
		clearReports(view, reports, fh.File().Identity().URI)
	}

	// If we have `go list` errors, we may want to offer a warning message to the user.
	var warningMsg string
	if hasListErrors(pkg.GetErrors()) {
		warningMsg, err = checkCommonErrors(ctx, view, f.URI())
		if err != nil {
			log.Error(ctx, "error checking common errors", err, telemetry.File.Of(f.URI))
		}
	}

	// Prepare any additional reports for the errors in this package.
	for _, err := range pkg.GetErrors() {
		if err.Kind != packages.ListError {
			continue
		}
		clearReports(view, reports, packagesErrorSpan(err).URI())
	}

	// Run diagnostics for the package that this URI belongs to.
	if !diagnostics(ctx, view, pkg, reports) {
		// If we don't have any list, parse, or type errors, run analyses.
		if err := analyses(ctx, view, cph, disabledAnalyses, reports); err != nil {
			log.Error(ctx, "failed to run analyses", err, telemetry.File.Of(f.URI()))
		}
	}
	// Updates to the diagnostics for this package may need to be propagated.
	revDeps := f.GetActiveReverseDeps(ctx)
	for _, f := range revDeps {
		cphs, err := f.CheckPackageHandles(ctx)
		if err != nil {
			return nil, "", err
		}
		cph := WidestCheckPackageHandle(cphs)
		pkg, err := cph.Check(ctx)
		if err != nil {
			return nil, warningMsg, err
		}
		for _, fh := range pkg.Files() {
			clearReports(view, reports, fh.File().Identity().URI)
		}
		diagnostics(ctx, view, pkg, reports)
	}
	return reports, warningMsg, nil
}

type diagnosticSet struct {
	listErrors, parseErrors, typeErrors []*Diagnostic
}

func diagnostics(ctx context.Context, view View, pkg Package, reports map[span.URI][]Diagnostic) bool {
	ctx, done := trace.StartSpan(ctx, "source.diagnostics", telemetry.Package.Of(pkg.ID()))
	defer done()

	diagSets := make(map[span.URI]*diagnosticSet)
	for _, err := range pkg.GetErrors() {
		spn := packagesErrorSpan(err)
		diag := &Diagnostic{
			URI:      spn.URI(),
			Message:  err.Msg,
			Source:   "LSP",
			Severity: SeverityError,
		}
		set, ok := diagSets[diag.URI]
		if !ok {
			set = &diagnosticSet{}
			diagSets[diag.URI] = set
		}
		switch err.Kind {
		case packages.ParseError:
			set.parseErrors = append(set.parseErrors, diag)
		case packages.TypeError:
			set.typeErrors = append(set.typeErrors, diag)
		default:
			set.listErrors = append(set.listErrors, diag)
		}
		rng, err := spanToRange(ctx, view, pkg, spn, err.Kind == packages.TypeError)
		if err != nil {
			log.Error(ctx, "failed to convert span to range", err)
			continue
		}
		diag.Range = rng
	}
	var nonEmptyDiagnostics bool // track if we actually send non-empty diagnostics
	for uri, set := range diagSets {
		// Don't report type errors if there are parse errors or list errors.
		diags := set.typeErrors
		if len(set.parseErrors) > 0 {
			diags = set.parseErrors
		} else if len(set.listErrors) > 0 {
			diags = set.listErrors
		}
		if len(diags) > 0 {
			nonEmptyDiagnostics = true
		}
		for _, diag := range diags {
			if _, ok := reports[uri]; ok {
				reports[uri] = append(reports[uri], *diag)
			}
		}
	}
	return nonEmptyDiagnostics
}

// spanToRange converts a span.Span to a protocol.Range,
// assuming that the span belongs to the package whose diagnostics are being computed.
func spanToRange(ctx context.Context, view View, pkg Package, spn span.Span, isTypeError bool) (protocol.Range, error) {
	ph, err := pkg.File(spn.URI())
	if err != nil {
		return protocol.Range{}, err
	}
	_, m, _, err := ph.Cached(ctx)
	if err != nil {
		return protocol.Range{}, err
	}
	data, _, err := ph.File().Read(ctx)
	if err != nil {
		return protocol.Range{}, err
	}
	// Try to get a range for the diagnostic.
	// TODO: Don't just limit ranges to type errors.
	if spn.IsPoint() && isTypeError {
		if s, err := spn.WithOffset(m.Converter); err == nil {
			start := s.Start()
			offset := start.Offset()
			if width := bytes.IndexAny(data[offset:], " \n,():;[]"); width > 0 {
				spn = span.New(spn.URI(), start, span.NewPoint(start.Line(), start.Column()+width, offset+width))
			}
		}
	}
	return m.Range(spn)
}

func analyses(ctx context.Context, view View, cph CheckPackageHandle, disabledAnalyses map[string]struct{}, reports map[span.URI][]Diagnostic) error {
	// Type checking and parsing succeeded. Run analyses.
	if err := runAnalyses(ctx, view, cph, disabledAnalyses, func(a *analysis.Analyzer, diag analysis.Diagnostic) error {
		diagnostic, err := toDiagnostic(ctx, view, diag, a.Name)
		if err != nil {
			return err
		}
		addReport(view, reports, diagnostic.URI, diagnostic)
		return nil
	}); err != nil {
		return err
	}
	return nil
}

func toDiagnostic(ctx context.Context, view View, diag analysis.Diagnostic, category string) (Diagnostic, error) {
	r := span.NewRange(view.Session().Cache().FileSet(), diag.Pos, diag.End)
	spn, err := r.Span()
	if err != nil {
		// The diagnostic has an invalid position, so we don't have a valid span.
		return Diagnostic{}, err
	}
	if diag.Category != "" {
		category += "." + category
	}
	f, err := view.GetFile(ctx, spn.URI())
	if err != nil {
		return Diagnostic{}, err
	}
	gof, ok := f.(GoFile)
	if !ok {
		return Diagnostic{}, errors.Errorf("%s is not a Go file", f.URI())
	}
	// If the package has changed since these diagnostics were computed,
	// this may be incorrect. Should the package be associated with the diagnostic?
	cphs, err := gof.CheckPackageHandles(ctx)
	if err != nil {
		return Diagnostic{}, err
	}
	cph := NarrowestCheckPackageHandle(cphs)
	pkg, err := cph.Cached(ctx)
	if err != nil {
		return Diagnostic{}, err
	}
	ca, err := getCodeActions(ctx, view, pkg, diag)
	if err != nil {
		return Diagnostic{}, err
	}

	rng, err := spanToRange(ctx, view, pkg, spn, false)
	if err != nil {
		return Diagnostic{}, err
	}
	return Diagnostic{
		URI:            spn.URI(),
		Range:          rng,
		Source:         category,
		Message:        diag.Message,
		Severity:       SeverityWarning,
		SuggestedFixes: ca,
	}, nil
}

func clearReports(v View, reports map[span.URI][]Diagnostic, uri span.URI) {
	if v.Ignore(uri) {
		return
	}
	reports[uri] = []Diagnostic{}
}

func addReport(v View, reports map[span.URI][]Diagnostic, uri span.URI, diagnostic Diagnostic) {
	if v.Ignore(uri) {
		return
	}
	if _, ok := reports[uri]; ok {
		reports[uri] = append(reports[uri], diagnostic)
	}
}

func packagesErrorSpan(err packages.Error) span.Span {
	if err.Pos == "" {
		return parseDiagnosticMessage(err.Msg)
	}
	return span.Parse(err.Pos)
}

// parseDiagnosticMessage attempts to parse a standard `go list` error message
// by stripping off the trailing error message.
//
// It works only on errors whose message is prefixed by colon,
// followed by a space (": "). For example:
//
//   attributes.go:13:1: expected 'package', found 'type'
//
func parseDiagnosticMessage(input string) span.Span {
	input = strings.TrimSpace(input)
	msgIndex := strings.Index(input, ": ")
	if msgIndex < 0 {
		return span.Parse(input)
	}
	return span.Parse(input[:msgIndex])
}

func singleDiagnostic(uri span.URI, format string, a ...interface{}) map[span.URI][]Diagnostic {
	return map[span.URI][]Diagnostic{
		uri: []Diagnostic{{
			Source:   "LSP",
			URI:      uri,
			Range:    protocol.Range{},
			Message:  fmt.Sprintf(format, a...),
			Severity: SeverityError,
		}},
	}
}

var Analyzers = []*analysis.Analyzer{
	// The traditional vet suite:
	asmdecl.Analyzer,
	assign.Analyzer,
	atomic.Analyzer,
	atomicalign.Analyzer,
	bools.Analyzer,
	buildtag.Analyzer,
	cgocall.Analyzer,
	composite.Analyzer,
	copylock.Analyzer,
	httpresponse.Analyzer,
	loopclosure.Analyzer,
	lostcancel.Analyzer,
	nilfunc.Analyzer,
	printf.Analyzer,
	shift.Analyzer,
	stdmethods.Analyzer,
	structtag.Analyzer,
	tests.Analyzer,
	unmarshal.Analyzer,
	unreachable.Analyzer,
	unsafeptr.Analyzer,
	unusedresult.Analyzer,
}

func runAnalyses(ctx context.Context, view View, cph CheckPackageHandle, disabledAnalyses map[string]struct{}, report func(a *analysis.Analyzer, diag analysis.Diagnostic) error) error {
	var analyzers []*analysis.Analyzer
	for _, a := range Analyzers {
		if _, ok := disabledAnalyses[a.Name]; ok {
			continue
		}
		analyzers = append(analyzers, a)
	}

	roots, err := analyze(ctx, view, []CheckPackageHandle{cph}, analyzers)
	if err != nil {
		return err
	}

	// Report diagnostics and errors from root analyzers.
	for _, r := range roots {
		var sdiags []Diagnostic
		for _, diag := range r.diagnostics {
			if r.err != nil {
				// TODO(matloob): This isn't quite right: we might return a failed prerequisites error,
				// which isn't super useful...
				return r.err
			}
			if err := report(r.Analyzer, diag); err != nil {
				return err
			}
			sdiag, err := toDiagnostic(ctx, view, diag, r.Analyzer.Name)
			if err != nil {
				return err
			}
			sdiags = append(sdiags, sdiag)
		}
		pkg, err := cph.Check(ctx)
		if err != nil {
			return err
		}
		pkg.SetDiagnostics(r.Analyzer, sdiags)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package qemuguesttools implements the command that runs inside a QEMU VM.
// These guest tools are responsible for fetching and executing the task
// command, as well as posting the log from the task command to the meta-data
// service.
//
// The guest tools are also responsible for polling the meta-data service for
// actions to do like list-folder, get-artifact or execute a new shell.
//
// The guest tools is pretty much the only way taskcluster-worker can talk to
// the guest virtual machine. As you can't execute processes inside a virtual
// machine without SSH'ing into it or something. That something is these
// guest tools.
package qemuguesttools

import (
	"io"
	"io/ioutil"
	"os"

	yaml "gopkg.in/yaml.v2"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/commands"
	"github.com/taskcluster/taskcluster-worker/runtime/monitoring"
	"github.com/taskcluster/taskcluster-worker/runtime/util"
)

var debug = util.Debug("guesttools")

func init() {
	commands.Register("qemu-guest-tools", cmd{})
}

type cmd struct{}

func (cmd) Summary() string {
	return "Run guest-tools, for use in VMs for the QEMU engine"
}

func (cmd) Usage() string {
	return `taskcluster-worker qemu-guest-tools start the guest tools that should
run inside the virtual machines used with QEMU engine.

The "run" (default) command will fetch a command to execute from the meta-data
service, upload the log and result as success/failed. The command will also
continuously poll the meta-data service for actions, such as put-artifact,
list-folder or start an interactive shell.

The "post-log" command will upload <log-file> to the meta-data service. If - is
given it will read the log from standard input. This command is useful as
meta-data can handle more than one log stream, granted they might get mangled.

Usage:
  taskcluster-worker qemu-guest-tools [options] [run]
  taskcluster-worker qemu-guest-tools [options] post-log [--] <log-file>

Options:
  -c, --config <file>  Load YAML configuration for file.
      --host <ip>      IP-address of meta-data server [default: 169.254.169.254].
  -h, --help           Show this screen.

Configuration:
  entrypoint: []                  # Wrapper command if any
  env:                            # Default environment variables
    HOME:     "/home/worker"
  shell:      ["bash", "-bash"]   # Default interactive system shell
  user:       "worker"            # User to run commands under
  workdir:    "/home/worker"      # Current working directory for commands
`
}

func (cmd) Execute(arguments map[string]interface{}) bool {
	monitor := monitoring.NewLoggingMonitor("info", nil, "").WithTag("component", "qemu-guest-tools")

	host := arguments["--host"].(string)
	configFile, _ := arguments["--config"].(string)

	// Load configuration
	var C config
	if configFile != "" {
		data, err := ioutil.ReadFile(configFile)
		if err != nil {
			monitor.Panicf("Failed to read configFile: %s, error: %s", configFile, err)
		}
		var c interface{}
		if err := yaml.Unmarshal(data, &c); err != nil {
			monitor.Panicf("Failed to parse configFile: %s, error: %s", configFile, err)
		}
		if err := configSchema.Validate(c); err != nil {
			monitor.Panicf("Invalid configFile: %s, error: %s", configFile, err)
		}
		schematypes.MustValidateAndMap(configSchema, c, &C)
	}

	// Create guest tools
	g := new(C, host, monitor)

	if arguments["post-log"].(bool) {
		logFile := arguments["<log-file>"].(string)
		var r io.Reader
		if logFile == "-" {
			r = os.Stdin
		} else {
			f, err := os.Open(logFile)
			if err != nil {
				monitor.Error("Failed to open log-file, error: ", err)
				return false
			}
			defer f.Close()
			r = f
		}
		w, done := g.CreateTaskLog()
		_, err := io.Copy(w, r)
		if err != nil {
			monitor.Error("Failed to post entire log, error: ", err)
		} else {
			err = w.Close()
			<-done
		}
		return err == nil
	}

	go g.Run()
	// Process actions forever, this must run in the main thread as exiting the
	// main thread will cause the go program to exit.
	g.ProcessActions()

	return true
}
<commit_msg>Convert to simple JSON types<commit_after>// Package qemuguesttools implements the command that runs inside a QEMU VM.
// These guest tools are responsible for fetching and executing the task
// command, as well as posting the log from the task command to the meta-data
// service.
//
// The guest tools are also responsible for polling the meta-data service for
// actions to do like list-folder, get-artifact or execute a new shell.
//
// The guest tools is pretty much the only way taskcluster-worker can talk to
// the guest virtual machine. As you can't execute processes inside a virtual
// machine without SSH'ing into it or something. That something is these
// guest tools.
package qemuguesttools

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"

	yaml "gopkg.in/yaml.v2"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/commands"
	"github.com/taskcluster/taskcluster-worker/runtime/monitoring"
	"github.com/taskcluster/taskcluster-worker/runtime/util"
)

var debug = util.Debug("guesttools")

func init() {
	commands.Register("qemu-guest-tools", cmd{})
}

type cmd struct{}

func (cmd) Summary() string {
	return "Run guest-tools, for use in VMs for the QEMU engine"
}

func (cmd) Usage() string {
	return `taskcluster-worker qemu-guest-tools start the guest tools that should
run inside the virtual machines used with QEMU engine.

The "run" (default) command will fetch a command to execute from the meta-data
service, upload the log and result as success/failed. The command will also
continuously poll the meta-data service for actions, such as put-artifact,
list-folder or start an interactive shell.

The "post-log" command will upload <log-file> to the meta-data service. If - is
given it will read the log from standard input. This command is useful as
meta-data can handle more than one log stream, granted they might get mangled.

Usage:
  taskcluster-worker qemu-guest-tools [options] [run]
  taskcluster-worker qemu-guest-tools [options] post-log [--] <log-file>

Options:
  -c, --config <file>  Load YAML configuration for file.
      --host <ip>      IP-address of meta-data server [default: 169.254.169.254].
  -h, --help           Show this screen.

Configuration:
  entrypoint: []                  # Wrapper command if any
  env:                            # Default environment variables
    HOME:     "/home/worker"
  shell:      ["bash", "-bash"]   # Default interactive system shell
  user:       "worker"            # User to run commands under
  workdir:    "/home/worker"      # Current working directory for commands
`
}

func (cmd) Execute(arguments map[string]interface{}) bool {
	monitor := monitoring.NewLoggingMonitor("info", nil, "").WithTag("component", "qemu-guest-tools")

	host := arguments["--host"].(string)
	configFile, _ := arguments["--config"].(string)

	// Load configuration
	var C config
	if configFile != "" {
		data, err := ioutil.ReadFile(configFile)
		if err != nil {
			monitor.Panicf("Failed to read configFile: %s, error: %s", configFile, err)
		}
		var c interface{}
		if err := yaml.Unmarshal(data, &c); err != nil {
			monitor.Panicf("Failed to parse configFile: %s, error: %s", configFile, err)
		}
		c = convertSimpleJSONTypes(c)
		if err := configSchema.Validate(c); err != nil {
			monitor.Panicf("Invalid configFile: %s, error: %s", configFile, err)
		}
		schematypes.MustValidateAndMap(configSchema, c, &C)
	}

	// Create guest tools
	g := new(C, host, monitor)

	if arguments["post-log"].(bool) {
		logFile := arguments["<log-file>"].(string)
		var r io.Reader
		if logFile == "-" {
			r = os.Stdin
		} else {
			f, err := os.Open(logFile)
			if err != nil {
				monitor.Error("Failed to open log-file, error: ", err)
				return false
			}
			defer f.Close()
			r = f
		}
		w, done := g.CreateTaskLog()
		_, err := io.Copy(w, r)
		if err != nil {
			monitor.Error("Failed to post entire log, error: ", err)
		} else {
			err = w.Close()
			<-done
		}
		return err == nil
	}

	go g.Run()
	// Process actions forever, this must run in the main thread as exiting the
	// main thread will cause the go program to exit.
	g.ProcessActions()

	return true
}

func convertSimpleJSONTypes(val interface{}) interface{} {
	switch val := val.(type) {
	case []interface{}:
		r := make([]interface{}, len(val))
		for i, v := range val {
			r[i] = convertSimpleJSONTypes(v)
		}
		return r
	case map[interface{}]interface{}:
		r := make(map[string]interface{})
		for k, v := range val {
			s, ok := k.(string)
			if !ok {
				s = fmt.Sprintf("%v", k)
			}
			r[s] = convertSimpleJSONTypes(v)
		}
		return r
	case int:
		return float64(val)
	default:
		return val
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2022 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package ssh

import (
	"bufio"
	"bytes"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/c-bata/go-prompt"
)

// TODO(blacknon): `!!`や`!$`についても実装を行う
// TODO(blacknon): `!command`だとまとめてパイプ経由でデータを渡すことになっているが、`!!command`で個別のローカルコマンドにデータを渡すように実装する

// Completer parallel-shell complete function
func (ps *pShell) Completer(t prompt.Document) []prompt.Suggest {
	// if currente line data is none.
	if len(t.CurrentLine()) == 0 {
		return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
	}

	// Get cursor left
	left := t.CurrentLineBeforeCursor()
	pslice, err := parsePipeLine(left)
	if err != nil {
		return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
	}

	// Get cursor char(string)
	char := ""
	if len(left) > 0 {
		char = string(left[len(left)-1])
	}

	sl := len(pslice) // pline slice count
	ll := 0
	num := 0
	if sl >= 1 {
		ll = len(pslice[sl-1])             // pline count
		num = len(pslice[sl-1][ll-1].Args) // pline args count
	}

	if sl >= 1 && ll >= 1 {
		c := pslice[sl-1][ll-1].Args[0]

		// switch suggest
		switch {
		case num <= 1 && !contains([]string{" ", "|"}, char): // if command
			var c []prompt.Suggest

			// build-in command suggest
			buildin := []prompt.Suggest{
				{Text: "exit", Description: "exit lssh shell"},
				{Text: "quit", Description: "exit lssh shell"},
				{Text: "clear", Description: "clear screen"},
				{Text: "%history", Description: "show history"},
				{Text: "%out", Description: "%out [num], show history result."},
				{Text: "%outlist", Description: "%outlist, show history result list."},
				// outの出力でdiffをするためのローカルコマンド。すべての出力と比較するのはあまりに辛いと思われるため、最初の出力との比較、といった方式で対応するのが良いか？？
				// {Text: "%diff", Description: "%diff [num], show history result list."},
			}
			c = append(c, buildin...)

			// get remote and local command complete data
			c = append(c, ps.CmdComplete...)

			// return
			return prompt.FilterHasPrefix(c, t.GetWordBeforeCursor(), false)

		case checkBuildInCommand(c): // if build-in command.
			var a []prompt.Suggest
			switch c {
			case "%out":
				for i := 0; i < len(ps.History); i++ {
					var cmd string
					for _, h := range ps.History[i] {
						cmd = h.Command
					}

					suggest := prompt.Suggest{
						Text:        strconv.Itoa(i),
						Description: cmd,
					}
					a = append(a, suggest)
				}
			}

			return prompt.FilterHasPrefix(a, t.GetWordBeforeCursor(), false)

		default:
			switch {
			case contains([]string{"/"}, char): // char is slach or
				ps.PathComplete = ps.GetPathComplete(!checkLocalCommand(c), t.GetWordBeforeCursor())
			case contains([]string{" "}, char) && strings.Count(t.CurrentLineBeforeCursor(), " ") == 1:
				ps.PathComplete = ps.GetPathComplete(!checkLocalCommand(c), t.GetWordBeforeCursor())
			}

			// get last slash place
			word := t.GetWordBeforeCursor()
			sp := strings.LastIndex(word, "/")
			if len(word) > 0 {
				word = word[sp+1:]
			}

			return prompt.FilterHasPrefix(ps.PathComplete, word, false)
		}
	}

	return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
}

// GetCommandComplete get command list remote machine.
// mode ... command/path
// data ... Value being entered
func (ps *pShell) GetCommandComplete() {
	// bash complete command. use `compgen`.
	compCmd := []string{"compgen", "-c"}
	command := strings.Join(compCmd, " ")

	// get local machine command complete
	local, _ := exec.Command("bash", "-c", command).Output()
	rd := strings.NewReader(string(local))
	sc := bufio.NewScanner(rd)
	for sc.Scan() {
		suggest := prompt.Suggest{
			Text:        "!" + sc.Text(),
			Description: "Command. from:localhost",
		}
		ps.CmdComplete = append(ps.CmdComplete, suggest)
	}

	// get remote machine command complete
	// command map
	cmdMap := map[string][]string{}

	// append command to cmdMap
	for _, c := range ps.Connects {
		// Create buffer
		buf := new(bytes.Buffer)

		// Create session, and output to buffer
		session, _ := c.CreateSession()
		session.Stdout = buf

		// Run get complete command
		session.Run(command)

		// Scan and put completed command to map.
		sc := bufio.NewScanner(buf)
		for sc.Scan() {
			cmdMap[sc.Text()] = append(cmdMap[sc.Text()], c.Name)
		}
	}

	// cmdMap to suggest
	for cmd, hosts := range cmdMap {
		// join hosts
		sort.Strings(hosts)
		h := strings.Join(hosts, ",")

		// create suggest
		suggest := prompt.Suggest{
			Text:        cmd,
			Description: "Command. from:" + h,
		}

		// append ps.Complete
		ps.CmdComplete = append(ps.CmdComplete, suggest)
	}

	sort.SliceStable(ps.CmdComplete, func(i, j int) bool { return ps.CmdComplete[i].Text < ps.CmdComplete[j].Text })
}

// GetPathComplete return complete path from local or remote machine.
// TODO(blacknon): 複数のノードにあるPATHだけ補完リストに出てる状態なので、単一ノードにしか無いファイルも出力されるよう修正する
func (ps *pShell) GetPathComplete(remote bool, word string) (p []prompt.Suggest) {
	compCmd := []string{"compgen", "-f", word}
	command := strings.Join(compCmd, " ")

	switch {
	case remote: // is remote machine
		// create map
		m := map[string][]string{}

		exit := make(chan bool)

		// create sync mutex
		sm := new(sync.Mutex)

		// append path to m
		for _, c := range ps.Connects {
			con := c
			go func() {
				// Create buffer
				buf := new(bytes.Buffer)

				// Create session, and output to buffer
				session, _ := c.CreateSession()
				session.Stdout = buf

				// Run get complete command
				session.Run(command)

				// Scan and put completed command to map.
				sc := bufio.NewScanner(buf)
				for sc.Scan() {
					sm.Lock()

					var path string
					if runtime.GOOS != "windows" {
						path = filepath.Base(sc.Text())
					} else {
						path = sc.Text()
					}
					m[path] = append(m[path], con.Name)
					sm.Unlock()
				}

				exit <- true
			}()
		}

		for i := 0; i < len(ps.Connects); i++ {
			<-exit
		}

		// m to suggest
		for path, hosts := range m {
			// join hosts
			h := strings.Join(hosts, ",")

			// create suggest
			suggest := prompt.Suggest{
				Text:        path,
				Description: "remote path. from:" + h,
			}

			// append ps.Complete
			p = append(p, suggest)
		}

	case !remote: // is local machine
		sgt, _ := exec.Command("bash", "-c", command).Output()
		rd := strings.NewReader(string(sgt))
		sc := bufio.NewScanner(rd)
		for sc.Scan() {
			suggest := prompt.Suggest{
				Text: filepath.Base(sc.Text()),
				// Text:        sc.Text(),
				Description: "local path.",
			}
			p = append(p, suggest)
		}
	}

	sort.SliceStable(p, func(i, j int) bool { return p[i].Text < p[j].Text })
	return
}

func contains(s []string, e string) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}
<commit_msg>update. pshell complete update<commit_after>// Copyright (c) 2022 Blacknon. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package ssh

import (
	"bufio"
	"bytes"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"

	"github.com/c-bata/go-prompt"
)

// TODO(blacknon): `!!`や`!$`についても実装を行う
// TODO(blacknon): `!command`だとまとめてパイプ経由でデータを渡すことになっているが、`!!command`で個別のローカルコマンドにデータを渡すように実装する

// Completer parallel-shell complete function
func (ps *pShell) Completer(t prompt.Document) []prompt.Suggest {
	// if currente line data is none.
	if len(t.CurrentLine()) == 0 {
		return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
	}

	// Get cursor left
	left := t.CurrentLineBeforeCursor()
	pslice, err := parsePipeLine(left)
	if err != nil {
		return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
	}

	// Get cursor char(string)
	char := ""
	if len(left) > 0 {
		char = string(left[len(left)-1])
	}

	sl := len(pslice) // pline slice count
	ll := 0
	num := 0
	if sl >= 1 {
		ll = len(pslice[sl-1])             // pline count
		num = len(pslice[sl-1][ll-1].Args) // pline args count
	}

	if sl >= 1 && ll >= 1 {
		c := pslice[sl-1][ll-1].Args[0]

		// switch suggest
		switch {
		case num <= 1 && !contains([]string{" ", "|"}, char): // if command
			var c []prompt.Suggest

			// build-in command suggest
			buildin := []prompt.Suggest{
				{Text: "exit", Description: "exit lssh shell"},
				{Text: "quit", Description: "exit lssh shell"},
				{Text: "clear", Description: "clear screen"},
				{Text: "%history", Description: "show history"},
				{Text: "%out", Description: "%out [num], show history result."},
				{Text: "%outlist", Description: "%outlist, show history result list."},
				// outの出力でdiffをするためのローカルコマンド。すべての出力と比較するのはあまりに辛いと思われるため、最初の出力との比較、といった方式で対応するのが良いか？？
				// {Text: "%diff", Description: "%diff [num], show history result list."},
			}
			c = append(c, buildin...)

			// get remote and local command complete data
			c = append(c, ps.CmdComplete...)

			// return
			return prompt.FilterHasPrefix(c, t.GetWordBeforeCursor(), false)

		case checkBuildInCommand(c): // if build-in command.
			var a []prompt.Suggest
			switch c {
			case "%out":
				for i := 0; i < len(ps.History); i++ {
					var cmd string
					for _, h := range ps.History[i] {
						cmd = h.Command
					}

					suggest := prompt.Suggest{
						Text:        strconv.Itoa(i),
						Description: cmd,
					}
					a = append(a, suggest)
				}
			}

			return prompt.FilterHasPrefix(a, t.GetWordBeforeCursor(), false)

		default:
			switch {
			case contains([]string{"/"}, char): // char is slach or
				ps.PathComplete = ps.GetPathComplete(!checkLocalCommand(c), t.GetWordBeforeCursor())
			case contains([]string{" "}, char) && strings.Count(t.CurrentLineBeforeCursor(), " ") == 1:
				ps.PathComplete = ps.GetPathComplete(!checkLocalCommand(c), t.GetWordBeforeCursor())
			}

			// get last slash place
			word := t.GetWordBeforeCursor()
			sp := strings.LastIndex(word, "/")
			if len(word) > 0 {
				word = word[sp+1:]
			}

			return prompt.FilterHasPrefix(ps.PathComplete, word, false)
		}
	}

	return prompt.FilterHasPrefix(nil, t.GetWordBeforeCursor(), false)
}

// GetCommandComplete get command list remote machine.
// mode ... command/path
// data ... Value being entered
func (ps *pShell) GetCommandComplete() {
	// bash complete command. use `compgen`.
	compCmd := []string{"compgen", "-c"}
	command := strings.Join(compCmd, " ")

	// get local machine command complete
	local, _ := exec.Command("bash", "-c", command).Output()
	rd := strings.NewReader(string(local))
	sc := bufio.NewScanner(rd)
	for sc.Scan() {
		suggest := prompt.Suggest{
			Text:        "!" + sc.Text(),
			Description: "Command. from:localhost",
		}
		ps.CmdComplete = append(ps.CmdComplete, suggest)
	}

	// get remote machine command complete
	// command map
	cmdMap := map[string][]string{}

	// append command to cmdMap
	for _, c := range ps.Connects {
		// Create buffer
		buf := new(bytes.Buffer)

		// Create session, and output to buffer
		session, _ := c.CreateSession()
		session.Stdout = buf

		// Run get complete command
		session.Run(command)

		// Scan and put completed command to map.
		sc := bufio.NewScanner(buf)
		for sc.Scan() {
			cmdMap[sc.Text()] = append(cmdMap[sc.Text()], c.Name)
		}
	}

	// cmdMap to suggest
	for cmd, hosts := range cmdMap {
		// join hosts
		sort.Strings(hosts)
		h := strings.Join(hosts, ",")

		// create suggest
		suggest := prompt.Suggest{
			Text:        cmd,
			Description: "Command. from:" + h,
		}

		// append ps.Complete
		ps.CmdComplete = append(ps.CmdComplete, suggest)
	}

	sort.SliceStable(ps.CmdComplete, func(i, j int) bool { return ps.CmdComplete[i].Text < ps.CmdComplete[j].Text })
}

// GetPathComplete return complete path from local or remote machine.
// TODO(blacknon): 複数のノードにあるPATHだけ補完リストに出てる状態なので、単一ノードにしか無いファイルも出力されるよう修正する
func (ps *pShell) GetPathComplete(remote bool, word string) (p []prompt.Suggest) {
	compCmd := []string{"compgen", "-f", word}
	command := strings.Join(compCmd, " ")

	switch {
	case remote: // is remote machine
		// create map
		m := map[string][]string{}

		exit := make(chan bool)

		// create sync mutex
		sm := new(sync.Mutex)

		// append path to m
		for _, c := range ps.Connects {
			con := c
			go func() {
				// Create buffer
				buf := new(bytes.Buffer)

				// Create session, and output to buffer
				session, _ := c.CreateSession()
				session.Stdout = buf

				// Run get complete command
				session.Run(command)

				// Scan and put completed command to map.
				sc := bufio.NewScanner(buf)
				for sc.Scan() {
					sm.Lock()

					var path string
					path = filepath.Base(sc.Text())
					if runtime.GOOS == "windows" {
						path = filepath.ToSlash(path)
					}
					m[path] = append(m[path], con.Name)
					sm.Unlock()
				}

				exit <- true
			}()
		}

		for i := 0; i < len(ps.Connects); i++ {
			<-exit
		}

		// m to suggest
		for path, hosts := range m {
			// join hosts
			h := strings.Join(hosts, ",")

			// create suggest
			suggest := prompt.Suggest{
				Text:        path,
				Description: "remote path. from:" + h,
			}

			// append ps.Complete
			p = append(p, suggest)
		}

	case !remote: // is local machine
		sgt, _ := exec.Command("bash", "-c", command).Output()
		rd := strings.NewReader(string(sgt))
		sc := bufio.NewScanner(rd)
		for sc.Scan() {
			suggest := prompt.Suggest{
				Text: filepath.Base(sc.Text()),
				// Text:        sc.Text(),
				Description: "local path.",
			}
			p = append(p, suggest)
		}
	}

	sort.SliceStable(p, func(i, j int) bool { return p[i].Text < p[j].Text })
	return
}

func contains(s []string, e string) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package bumpCmd

import (
	// Stdlib
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/app/appflags"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git/gitutil"
	"github.com/salsaflow/salsaflow/version"

	// Other
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: "bump [-commit] VERSION",
	Short:     "bump version to the specified value",
	Long: `
  Bump the version string to the specified value.

  In case -commit is set, the changes are committed as well.
  The repository must be clean for the commit to be created.
	`,
	Action: run,
}

var (
	flagCommit bool
)

func init() {
	// Register flags.
	Command.Flags.BoolVar(&flagCommit, "commit", flagCommit,
		"commit the new version string")

	// Register global flags.
	appflags.RegisterGlobalFlags(&Command.Flags)
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		os.Exit(2)
	}

	if err := runMain(args[0]); err != nil {
		errs.Fatal(err)
	}
}

func runMain(versionString string) error {
	// Make sure the version string is correct.
	ver, err := version.Parse(versionString)
	if err != nil {
		return err
	}

	// In case -commit is set, set and commit the version string.
	if flagCommit {
		currentBranch, err := gitutil.CurrentBranch()
		if err != nil {
			return err
		}

		_, err = version.SetForBranch(ver, currentBranch)
		return err
	}

	// Otherwise just set the version.
	return version.Set(ver)
}
<commit_msg>version bump: Improve error message<commit_after>package bumpCmd

import (
	// Stdlib
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/app/appflags"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git/gitutil"
	"github.com/salsaflow/salsaflow/version"

	// Other
	"gopkg.in/tchap/gocli.v2"
)

var Command = &gocli.Command{
	UsageLine: "bump [-commit] VERSION",
	Short:     "bump version to the specified value",
	Long: `
  Bump the version string to the specified value.

  In case -commit is set, the changes are committed as well.
  The repository must be clean for the commit to be created.
	`,
	Action: run,
}

var (
	flagCommit bool
)

func init() {
	// Register flags.
	Command.Flags.BoolVar(&flagCommit, "commit", flagCommit,
		"commit the new version string")

	// Register global flags.
	appflags.RegisterGlobalFlags(&Command.Flags)
}

func run(cmd *gocli.Command, args []string) {
	if len(args) != 1 {
		cmd.Usage()
		os.Exit(2)
	}

	if err := runMain(args[0]); err != nil {
		errs.Fatal(err)
	}
}

func runMain(versionString string) error {
	// Make sure the version string is correct.
	task := "Parse the command line VERSION argument"
	ver, err := version.Parse(versionString)
	if err != nil {
		hint := `
The version string must be in the form of Major.Minor.Patch
and no part of the version string can be omitted.

`
		return errs.NewErrorWithHint(task, err, hint)
	}

	// In case -commit is set, set and commit the version string.
	if flagCommit {
		currentBranch, err := gitutil.CurrentBranch()
		if err != nil {
			return err
		}

		_, err = version.SetForBranch(ver, currentBranch)
		return err
	}

	// Otherwise just set the version.
	return version.Set(ver)
}
<|endoftext|>