func main() {
	if version == "" {
		version = "dev"
	}

	logger := log.New(os.Stderr, "", log.LstdFlags)

	logger.Printf("PivNet Resource version: %s", version)

	if len(os.Args) < 2 {
		log.Fatalf("not enough args - usage: %s <sources directory>", os.Args[0])
	}

	downloadDir := os.Args[1]

	var input concourse.InRequest
	err := json.NewDecoder(os.Stdin).Decode(&input)
	if err != nil {
		log.Fatalln(err)
	}

	sanitized := concourse.SanitizedSource(input.Source)
	logger.SetOutput(sanitizer.NewSanitizer(sanitized, os.Stderr))

	verbose := false
	ls := logshim.NewLogShim(logger, logger, verbose)

	logger.Printf("Creating download directory: %s", downloadDir)

	err = os.MkdirAll(downloadDir, os.ModePerm)
	if err != nil {
		log.Fatalf("Exiting with error: %s", err)
	}

	err = validator.NewInValidator(input).Validate()
	if err != nil {
		log.Fatalf("Exiting with error: %s", err)
	}

	var endpoint string
	if input.Source.Endpoint != "" {
		endpoint = input.Source.Endpoint
	} else {
		endpoint = pivnet.DefaultHost
	}

	clientConfig := pivnet.ClientConfig{
		Host:      endpoint,
		Token:     input.Source.APIToken,
		UserAgent: useragent.UserAgent(version, "get", input.Source.ProductSlug),
	}

	client := gp.NewClient(
		clientConfig,
		ls,
	)

	d := downloader.NewDownloader(client, downloadDir, ls, os.Stderr)
	fs := md5sum.NewFileSummer()

	f := filter.NewFilter(ls)

	fileWriter := filesystem.NewFileWriter(downloadDir, ls)

	response, err := in.NewInCommand(
		ls,
		client,
		f,
		d,
		fs,
		fileWriter,
	).Run(input)
	if err != nil {
		log.Fatalf("Exiting with error: %s", err)
	}

	err = json.NewEncoder(os.Stdout).Encode(response)
	if err != nil {
		log.Fatalf("Exiting with error: %s", err)
	}
}
func main() {
	if version == "" {
		version = "dev"
	}

	logger := log.New(os.Stderr, "", log.LstdFlags|log.Lmicroseconds)

	logger.Printf("PivNet Resource version: %s", version)

	if len(os.Args) < 2 {
		log.Fatalf("not enough args - usage: %s <sources directory>", os.Args[0])
	}

	sourcesDir := os.Args[1]

	outDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatalln(err)
	}

	var input concourse.OutRequest

	err = json.NewDecoder(os.Stdin).Decode(&input)
	if err != nil {
		log.Fatalln(err)
	}

	sanitized := concourse.SanitizedSource(input.Source)
	logger.SetOutput(sanitizer.NewSanitizer(sanitized, os.Stderr))

	verbose := false
	ls := logshim.NewLogShim(logger, logger, verbose)

	var endpoint string
	if input.Source.Endpoint != "" {
		endpoint = input.Source.Endpoint
	} else {
		endpoint = pivnet.DefaultHost
	}

	clientConfig := pivnet.ClientConfig{
		Host:      endpoint,
		Token:     input.Source.APIToken,
		UserAgent: useragent.UserAgent(version, "put", input.Source.ProductSlug),
	}

	client := gp.NewClient(
		clientConfig,
		ls,
	)

	bucket := input.Source.Bucket
	if bucket == "" {
		bucket = defaultBucket
	}

	region := input.Source.Region
	if region == "" {
		region = defaultRegion
	}

	s3Client := s3.NewClient(s3.NewClientConfig{
		AccessKeyID:     input.Source.AccessKeyID,
		SecretAccessKey: input.Source.SecretAccessKey,
		RegionName:      region,
		Bucket:          bucket,
		Stderr:          os.Stderr,
		Logger:          ls,
	})

	uploaderClient := uploader.NewClient(uploader.Config{
		FilepathPrefix: input.Params.FilepathPrefix,
		SourcesDir:     sourcesDir,
		Transport:      s3Client,
	})

	globber := globs.NewGlobber(globs.GlobberConfig{
		FileGlob:   input.Params.FileGlob,
		SourcesDir: sourcesDir,
		Logger:     ls,
	})

	skipUpload := input.Params.FileGlob == "" && input.Params.FilepathPrefix == ""

	var m metadata.Metadata
	if input.Params.MetadataFile == "" {
		log.Fatalf("params.metadata_file must be provided")
	}

	metadataFilepath := filepath.Join(sourcesDir, input.Params.MetadataFile)
	metadataBytes, err := ioutil.ReadFile(metadataFilepath)
	if err != nil {
		log.Fatalf("params.metadata_file could not be read: %s", err.Error())
	}

	err = yaml.Unmarshal(metadataBytes, &m)
	if err != nil {
		log.Fatalf("params.metadata_file could not be parsed: %s", err.Error())
	}

	err = m.Validate()
	if err != nil {
		log.Fatalf("params.metadata_file is invalid: %s", err.Error())
	}

	validation := validator.NewOutValidator(input)
	semverConverter := semver.NewSemverConverter(ls)
	md5summer := md5sum.NewFileSummer()

	f := filter.NewFilter(ls)

	releaseCreator := release.NewReleaseCreator(
		client,
		semverConverter,
		ls,
		m,
		input.Params,
		input.Source,
		sourcesDir,
		input.Source.ProductSlug,
	)

	asyncTimeout := 1 * time.Hour
	pollFrequency := 5 * time.Second
	releaseUploader := release.NewReleaseUploader(
		uploaderClient,
		client,
		ls,
		md5summer,
		m,
		sourcesDir,
		input.Source.ProductSlug,
		asyncTimeout,
		pollFrequency,
	)

	releaseUserGroupsUpdater := release.NewUserGroupsUpdater(
		ls,
		client,
		m,
		input.Source.ProductSlug,
	)

	releaseDependenciesAdder := release.NewReleaseDependenciesAdder(
		ls,
		client,
		m,
		input.Source.ProductSlug,
	)

	releaseUpgradePathsAdder := release.NewReleaseUpgradePathsAdder(
		ls,
		client,
		m,
		input.Source.ProductSlug,
		f,
	)

	releaseFinalizer := release.NewFinalizer(
		client,
		ls,
		input.Params,
		m,
		sourcesDir,
		input.Source.ProductSlug,
	)

	outCmd := out.NewOutCommand(out.OutCommandConfig{
		Logger:                   ls,
		OutDir:                   outDir,
		SourcesDir:               sourcesDir,
		GlobClient:               globber,
		Validation:               validation,
		Creator:                  releaseCreator,
		Uploader:                 releaseUploader,
		UserGroupsUpdater:        releaseUserGroupsUpdater,
		ReleaseDependenciesAdder: releaseDependenciesAdder,
		ReleaseUpgradePathsAdder: releaseUpgradePathsAdder,
		Finalizer:                releaseFinalizer,
		M:                        m,
		SkipUpload:               skipUpload,
	})

	response, err := outCmd.Run(input)
	if err != nil {
		log.Fatalln(err)
	}

	err = json.NewEncoder(os.Stdout).Encode(response)
	if err != nil {
		log.Fatalln(err)
	}
}
			fileSummer *md5sum.FileSummer
		)

		BeforeEach(func() {
			var err error
			tempDir, err = ioutil.TempDir("", "")
			Expect(err).NotTo(HaveOccurred())

			fileContents = []byte("foobar contents")

			tempFilePath = filepath.Join(tempDir, "foobar")

			ioutil.WriteFile(tempFilePath, fileContents, os.ModePerm)

			fileSummer = md5sum.NewFileSummer()
		})

		AfterEach(func() {
			err := os.RemoveAll(tempDir)
			Expect(err).NotTo(HaveOccurred())
		})

		It("returns the MD5 of a file without error", func() {
			md5, err := fileSummer.SumFile(tempFilePath)
			Expect(err).NotTo(HaveOccurred())

			// Expected md5 of 'foobar contents'
			Expect(md5).To(Equal("fdd3d599138fd15d7673f3d3539531c1"))
		})