Skip to content

Commit

Permalink
rewrite step in golang
Browse files Browse the repository at this point in the history
  • Loading branch information
AndroidGuyDD committed Sep 5, 2017
1 parent 16db736 commit 5895c78
Show file tree
Hide file tree
Showing 16 changed files with 691 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.bitrise*
.gows.user.yml
.vscode/*
15 changes: 15 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Godeps/Readme

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions _tmp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# major change
* test change
* test change
# minor change
* test change
* test change
3 changes: 3 additions & 0 deletions _tmp/SecondChangeLog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Some addtional change log
- test
- test
1 change: 1 addition & 0 deletions _tmp/asset/test_asset.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Some asset file.
28 changes: 13 additions & 15 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

app:
envs:
# An example secret param, define it (A_SECRET_PARAM) in .bitrise.secrets.yml
- A_SECRET_PARAM: $A_SECRET_PARAM
# define those env vars in your .bitrise.secrets.yml
- TEST_AUTH_TOKEN: "$TEST_AUTH_TOKEN"
- TEST_REPOSITORY_URL: $TEST_REPOSITORY_URL
- BITRISE_BUILD_NUMBER: $TEST_BITRISE_BUILD_NUMBER
# If you want to share this step into a StepLib
- BITRISE_STEP_ID: github-release
- BITRISE_STEP_VERSION: "0.0.1"
Expand All @@ -14,12 +16,6 @@ app:
workflows:
test:
steps:
- script:
inputs:
- content: |
#!/bin/bash
echo "Just an example 'secrets' print."
echo "The value of 'A_SECRET_PARAM' is: $A_SECRET_PARAM"
- change-workdir:
title: Switch working dir to test / _tmp dir
description: |-
Expand All @@ -34,18 +30,20 @@ workflows:
- path::./:
title: Step Test
description: |-
The example input has a default value,
you can overwrite it if you want to, just like we did below,
but the step would use the default value specified in the `step.yml`
file if you would not specify another value.
run_if: true
Specify values
inputs:
- example_step_input: Example Step Input's value
- github_auth_token: $TEST_AUTH_TOKEN
- repository_url: $TEST_REPOSITORY_URL
- changelog_file_list: "CHANGELOG.md|SecondChangeLog.txt"
- is_draft: "true"
- release_name: "MyRelease $BITRISE_BUILD_NUMBER"
- release_tag: "v_$BITRISE_BUILD_NUMBER"
- upload_asset_file: "asset/test_asset.txt"
- script:
inputs:
- content: |
#!/bin/bash
echo "This output was generated by the Step (EXAMPLE_STEP_OUTPUT): $EXAMPLE_STEP_OUTPUT"
echo "This output was generated by the Step (RELEASE_URL): $RELEASE_URL"
# ----------------------------------------------------------------
Expand Down
261 changes: 261 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package main

import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"mime"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/bitrise-io/go-utils/log"
)

type ConfigModel struct {
GitHubAuthToken string
RepositoryURL string
ChangelogFileList string
ReleaseTag string
ReleaseName string
TargetCommitish string
IsDraft bool
IsPrerelease bool
UploadAssetFile string
}

type GitHubApiConfig struct {
User string
Repo string
AuthToken string
}

type GitHubRelease struct {
ID int `json:"id,omitempty"`
TagName string `json:"tag_name,omitempty"`
Name string `json:"name,omitempty"`
TargetCommitish string `json:"target_commitish,omitempty"`
Body string `json:"body,omitempty"`
Draft bool `json:"draft,omitempty"`
Prerelease bool `json:"prerelease,omitempty"`
UploadURL string `json:"upload_url,omitempty"`
HTMLURL string `json:"html_url,omitempty"`
}

var gitAPIRegexp = regexp.MustCompile(`([A-Za-z0-9]+@|http(|s)\:\/\/)([A-Za-z0-9.-]+)(:|\/)([^.]+)\/([^.]+)(\.git)?`)

const gitHubBaseURL = "https://api.github.com"
const gitHubUploadURL = "https://uploads.github.com"
const defaultMediaType = "application/octet-stream"

func createConfigsModelFromEnvs() ConfigModel {
return ConfigModel{
GitHubAuthToken: os.Getenv("github_auth_token"),
RepositoryURL: os.Getenv("repository_url"),
ChangelogFileList: os.Getenv("changelog_file_list"),
ReleaseTag: os.Getenv("release_tag"),
ReleaseName: os.Getenv("release_name"),
TargetCommitish: os.Getenv("target_commitish"),
IsDraft: os.Getenv("is_draft") == "true",
IsPrerelease: os.Getenv("is_prerelease") == "true",
UploadAssetFile: os.Getenv("upload_asset_file"),
}
}

func (configs ConfigModel) print() {
log.Infof("Configs:")
log.Printf("- GitHubAuthToken: %s", configs.GitHubAuthToken)
log.Printf("- RepositoryURL: %s", configs.RepositoryURL)
log.Printf("- ChangelogFileList: %s", configs.ChangelogFileList)
log.Printf("- ReleaseTag: %s", configs.ReleaseTag)
log.Printf("- ReleaseName: %s", configs.ReleaseName)
log.Printf("- TargetCommitish: %s", configs.TargetCommitish)
log.Printf("- IsDraft: %v", configs.IsDraft)
log.Printf("- IsPrerelease: %v", configs.IsPrerelease)
log.Printf("- UploadAssetFile: %v", configs.UploadAssetFile)
}

func (apiConfig GitHubApiConfig) print() {
log.Infof("ApiConfig:")
log.Printf("- User: %s", apiConfig.User)
log.Printf("- Repository: %s", apiConfig.Repo)
log.Printf("- AuthToken: %s", apiConfig.AuthToken)
}

func (apiConfig GitHubApiConfig) getCreateReleasesURL() string {
return gitHubBaseURL +
"/repos/" + apiConfig.User + "/" + apiConfig.Repo +
"/releases?access_token=" + apiConfig.AuthToken
}

func (apiConfig GitHubApiConfig) getUploadAssetURL(releaseId int, name string) string {
return gitHubUploadURL +
"/repos/" + apiConfig.User + "/" + apiConfig.Repo +
"/releases/" + strconv.Itoa(releaseId) + "/assets?access_token=" + apiConfig.AuthToken +
"&name=" + name
}

func inferGithubAPIConfig(config ConfigModel) (GitHubApiConfig, error) {
var apiConf GitHubApiConfig

match := gitAPIRegexp.FindStringSubmatch(config.RepositoryURL)
if len(match) < 7 {
return apiConf, errors.New("error: User and Repo could not be obtained")
}

apiConf = GitHubApiConfig{
User: match[5],
Repo: match[6],
AuthToken: config.GitHubAuthToken,
}
return apiConf, nil
}

func collectReleaseNotes(files string) string {
var buffer bytes.Buffer

for i, item := range strings.Split(files, "|") {
fileContent, err := ioutil.ReadFile(strings.TrimSpace(item))
if err != nil {
log.Errorf("%v", err)
continue
}

if i > 0 {
buffer.WriteString("\n")
}

buffer.Write(fileContent)
}

return buffer.String()
}

func failf(format string, v ...interface{}) {
log.Errorf(format, v...)
os.Exit(1)
}

func createRelease(config ConfigModel, releaseNotes string) GitHubRelease {
return GitHubRelease{
Name: config.ReleaseName,
TagName: config.ReleaseTag,
Draft: config.IsDraft,
Prerelease: config.IsPrerelease,
TargetCommitish: config.TargetCommitish,
Body: releaseNotes,
}
}

func postAsset(apiConf GitHubApiConfig, release *GitHubRelease, postAsset *os.File) error {
defer postAsset.Close()

stat, err := postAsset.Stat()
if err != nil {
return err
}

if stat.IsDir() {
return errors.New("asset can't be a directory")
}

uploadURL := apiConf.getUploadAssetURL(release.ID, filepath.Base(postAsset.Name()))
mediaType := mime.TypeByExtension(filepath.Ext(postAsset.Name()))
if mediaType == "" {
mediaType = defaultMediaType
}

log.Infof("Posting asset to %s", uploadURL)

hc := http.Client{}
req, err := http.NewRequest("POST", uploadURL, postAsset)
if err != nil {
return err
}

req.ContentLength = stat.Size()
req.Header.Set("Content-Type", mediaType)
resp, err := hc.Do(req)
if err != nil {
return err
}
if resp.StatusCode != 201 {
return errors.New("fileupload failed with " + resp.Status)
}

return nil
}

func postRelease(url string, release *GitHubRelease) error {
jsonRelease, err := json.Marshal(release)
if err != nil {
return err
}
log.Printf(string(jsonRelease))

log.Infof("Posting Release to: %v", url)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonRelease))
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != 201 {
return errors.New("GitHub API could not create release")
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

err = json.Unmarshal(body, &release)
if err != nil {
return err
}

return nil
}

func main() {

config := createConfigsModelFromEnvs()
config.print()

gitHubAPIConfig, err := inferGithubAPIConfig(config)
if err != nil {
failf("Failed to infer GitHub API config")
}
gitHubAPIConfig.print()

releaseNotes := collectReleaseNotes(config.ChangelogFileList)

release := createRelease(config, releaseNotes)
err = postRelease(gitHubAPIConfig.getCreateReleasesURL(), &release)
if err != nil {
failf("Failed to create Github release entry with error: %v", err)
}

if config.UploadAssetFile != "" {
uploadFile, err := os.Open(config.UploadAssetFile)
if err != nil {
log.Errorf("%v", err)
} else {
err = postAsset(gitHubAPIConfig, &release, uploadFile)
if err != nil {
log.Errorf("%v", err)
}
}
}

cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", "RELEASE_URL", "--value", release.HTMLURL).CombinedOutput()
if err != nil {
failf("Failed to expose output with envman, error: %#v | output: %s", err, cmdLog)
}

}
22 changes: 0 additions & 22 deletions step.sh

This file was deleted.

Loading

0 comments on commit 5895c78

Please sign in to comment.