release-v1
This commit is contained in:
commit
a5d76d8d5a
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Debian GNU/Linux 10 (1.13.10-buster)
|
||||||
|
FROM golang:1.13.10-buster
|
||||||
|
|
||||||
|
# copy entrypoint file
|
||||||
|
COPY entrypoint.go /usr/bin/entrypoint.go
|
||||||
|
|
||||||
|
# change mode of the entrypoint file
|
||||||
|
RUN chmod +x /usr/bin/entrypoint.go
|
||||||
|
|
||||||
|
# set entrypoint command
|
||||||
|
ENTRYPOINT [ "go", "run", "/usr/bin/entrypoint.go" ]
|
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# GitHub Action: go-build-action
|
||||||
|
This actions generates cross-platform executable files from a Go module.
|
||||||
|
|
||||||
|
![release](/assets/release.png)
|
||||||
|
|
||||||
|
# Workflow setup
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# workflow name
|
||||||
|
name: Generate release-artifacts
|
||||||
|
|
||||||
|
# on events
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- created
|
||||||
|
|
||||||
|
# workflow tasks
|
||||||
|
jobs:
|
||||||
|
generate:
|
||||||
|
name: Generate cross-platform builds
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Generate build files
|
||||||
|
uses: thatisuday/go-cross-build@v1
|
||||||
|
with:
|
||||||
|
platforms: 'linux/amd64, darwin/amd64, windows/amd64'
|
||||||
|
package: 'demo'
|
||||||
|
name: 'program'
|
||||||
|
compress: 'true'
|
||||||
|
dest: 'dist'
|
||||||
|
```
|
||||||
|
|
||||||
|
### option: **platforms**
|
||||||
|
The `platforms` option specifies comma-separated platform names to create binary-executable files for. To see the list of supported platforms, use `go tool dist list` command.
|
||||||
|
|
||||||
|
### option: **package**
|
||||||
|
If the module (_repository_) itself is a Go package, then `package` option value should be an empty string (''). If the repository contains a package directory, then `package` value should be the directory name.
|
||||||
|
|
||||||
|
### option: **compress**
|
||||||
|
The `compress` option if set to `'true'` will generate **compressed-tar** archive files for the each platform-build file. The resulting archive file also contains `README.md` and `LICENSE` file if they exist inside the root of the repository. In this mode, the binary executable file name is taken from the `name` option value.
|
||||||
|
|
||||||
|
### option: **name**
|
||||||
|
The `name` option sets a prefix for the build filenames. In compression mode, this prefix is applied to archive files and binary executable filename is set to this value.
|
||||||
|
|
||||||
|
### option: **dest**
|
||||||
|
The `dest` option sets the output directory for the build files. This should be a relative directory without leading `./`.
|
43
action.yml
Normal file
43
action.yml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# action name
|
||||||
|
name: 'go-cross-build'
|
||||||
|
|
||||||
|
# action author
|
||||||
|
author: 'Uday Hiwarale <thatisuday@gmail.com>'
|
||||||
|
|
||||||
|
# action description
|
||||||
|
description: 'Generates cross-platform executable files from a Go module.'
|
||||||
|
|
||||||
|
# action input values
|
||||||
|
inputs:
|
||||||
|
platforms:
|
||||||
|
description: 'Comma-separated list of "<kernel>/<arch>" combinations.'
|
||||||
|
default: 'linux/386,linux/amd64'
|
||||||
|
required: false
|
||||||
|
package:
|
||||||
|
description: 'Package (directory) in the module to build. By default, builds the module directory.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
|
compress:
|
||||||
|
description: 'Compress each build file inside a ".tar.gz" archive.'
|
||||||
|
default: 'false'
|
||||||
|
required: false
|
||||||
|
name:
|
||||||
|
description: 'Binary executable filename and filenames prefix for the build files.'
|
||||||
|
default: 'program'
|
||||||
|
required: false
|
||||||
|
dest:
|
||||||
|
description: 'Destination directory inside workspace to output build-artifacts.'
|
||||||
|
default: 'build'
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# action runner (golang:latest image)
|
||||||
|
runs:
|
||||||
|
using: 'docker'
|
||||||
|
image: 'Dockerfile'
|
||||||
|
env:
|
||||||
|
GO111MODULE: 'on'
|
||||||
|
|
||||||
|
# branding
|
||||||
|
branding:
|
||||||
|
icon: terminal
|
||||||
|
color: green
|
BIN
assets/release.png
Normal file
BIN
assets/release.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
215
entrypoint.go
Normal file
215
entrypoint.go
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*************************************/
|
||||||
|
|
||||||
|
// check if file exists (relative to the current directory)
|
||||||
|
func fileExists(path string) bool {
|
||||||
|
|
||||||
|
// get current directory
|
||||||
|
cwd, _ := os.Getwd()
|
||||||
|
|
||||||
|
// get an absolute path of the file
|
||||||
|
absPath := filepath.Join(cwd, path)
|
||||||
|
|
||||||
|
// access file information
|
||||||
|
if _, err := os.Stat(absPath); err != nil {
|
||||||
|
return !os.IsNotExist(err) // return `false` if doesn't exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// file exists
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy file using `cp` command
|
||||||
|
func copyFile(src, dest string) {
|
||||||
|
if err := exec.Command("cp", src, dest).Run(); err != nil {
|
||||||
|
fmt.Println("An error occurred during copy operation:", src, "=>", dest)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************/
|
||||||
|
|
||||||
|
// build the package for a platform
|
||||||
|
func build(packageName, destDir string, platform map[string]string, compress bool) {
|
||||||
|
|
||||||
|
// platform config
|
||||||
|
platformKernel := platform["kernel"]
|
||||||
|
platformArch := platform["arch"]
|
||||||
|
|
||||||
|
// binary executable file path
|
||||||
|
inputName := os.Getenv("INPUT_NAME")
|
||||||
|
|
||||||
|
// build file name (same as the `inputName` if compression is enabled)
|
||||||
|
buildFileName := fmt.Sprintf("%s-%s-%s", inputName, platformKernel, platformArch)
|
||||||
|
if compress {
|
||||||
|
buildFileName = inputName
|
||||||
|
}
|
||||||
|
|
||||||
|
// append `.exe` file-extension for windows
|
||||||
|
if platformKernel == "windows" {
|
||||||
|
buildFileName += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// workspace directory
|
||||||
|
workspaceDir := os.Getenv("GITHUB_WORKSPACE")
|
||||||
|
|
||||||
|
// destination directory path
|
||||||
|
destDirPath := filepath.Join(workspaceDir, destDir)
|
||||||
|
|
||||||
|
// join destination path
|
||||||
|
buildFilePath := filepath.Join(destDirPath, buildFileName)
|
||||||
|
|
||||||
|
// package directory local path
|
||||||
|
var packagePath string
|
||||||
|
if packageName == "" {
|
||||||
|
packagePath = "."
|
||||||
|
} else {
|
||||||
|
packagePath = "./" + packageName
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------*/
|
||||||
|
|
||||||
|
// command-line options for the `go build` command
|
||||||
|
buildOptions := []string{"build", "-buildmode", "exe", "-o", buildFilePath, packagePath}
|
||||||
|
|
||||||
|
// generate `go build` command
|
||||||
|
buildCmd := exec.Command("go", buildOptions...)
|
||||||
|
|
||||||
|
// set environment variables
|
||||||
|
buildCmd.Env = append(os.Environ(), []string{
|
||||||
|
fmt.Sprintf("GOOS=%s", platformKernel),
|
||||||
|
fmt.Sprintf("GOARCH=%s", platformArch),
|
||||||
|
}...)
|
||||||
|
|
||||||
|
// execute `go build` command
|
||||||
|
fmt.Println("Creating a build using :", buildCmd.String())
|
||||||
|
if output, err := buildCmd.Output(); err != nil {
|
||||||
|
fmt.Println("An error occurred during build:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------*/
|
||||||
|
|
||||||
|
// create a compressed `.tar.gz` file
|
||||||
|
if compress {
|
||||||
|
|
||||||
|
// compressed gzip file name
|
||||||
|
gzFileName := fmt.Sprintf("%s-%s-%s.tar.gz", inputName, platformKernel, platformArch)
|
||||||
|
|
||||||
|
/*------------*/
|
||||||
|
|
||||||
|
// file to compress (default: build file)
|
||||||
|
includeFiles := []string{buildFileName}
|
||||||
|
|
||||||
|
// copy "README.md" file inside destination directory
|
||||||
|
if fileExists("README.md") {
|
||||||
|
copyFile("README.md", filepath.Join(destDirPath, "README.md"))
|
||||||
|
includeFiles = append(includeFiles, "README.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy "LICENSE" file inside destination directory
|
||||||
|
if fileExists("LICENSE") {
|
||||||
|
copyFile("LICENSE", filepath.Join(destDirPath, "LICENSE"))
|
||||||
|
includeFiles = append(includeFiles, "LICENSE")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------*/
|
||||||
|
|
||||||
|
// command-line options for the `tar` command
|
||||||
|
tarOptions := append([]string{"-cvzf", gzFileName}, includeFiles...)
|
||||||
|
|
||||||
|
// generate `tar` command
|
||||||
|
tarCmd := exec.Command("tar", tarOptions...)
|
||||||
|
|
||||||
|
// set working directory for the command
|
||||||
|
tarCmd.Dir = destDirPath
|
||||||
|
|
||||||
|
// execute `tar` command
|
||||||
|
fmt.Println("Compressing build file using:", tarCmd.String())
|
||||||
|
if err := tarCmd.Run(); err != nil {
|
||||||
|
fmt.Println("An error occurred during compression:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------*/
|
||||||
|
|
||||||
|
// generate cleanup command
|
||||||
|
cleanCmd := exec.Command("rm", append([]string{"-f"}, includeFiles...)...)
|
||||||
|
|
||||||
|
// set working directory for the command
|
||||||
|
cleanCmd.Dir = destDirPath
|
||||||
|
|
||||||
|
// start cleanup process
|
||||||
|
fmt.Println("Performing cleanup operation using:", cleanCmd.String())
|
||||||
|
if err := cleanCmd.Run(); err != nil {
|
||||||
|
fmt.Println("An error occurred during cleaup:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************/
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// get input variables from action
|
||||||
|
inputPlatforms := os.Getenv("INPUT_PLATFORMS")
|
||||||
|
inputPackage := os.Getenv("INPUT_PACKAGE")
|
||||||
|
inputCompress := os.Getenv("INPUT_COMPRESS")
|
||||||
|
inputDest := os.Getenv("INPUT_DEST")
|
||||||
|
|
||||||
|
// package name to build
|
||||||
|
packageName := strings.ReplaceAll(inputPackage, " ", "")
|
||||||
|
|
||||||
|
// destination directory
|
||||||
|
destDir := strings.ReplaceAll(inputDest, " ", "")
|
||||||
|
|
||||||
|
// split platform names by comma (`,`)
|
||||||
|
platforms := strings.Split(inputPlatforms, ",")
|
||||||
|
|
||||||
|
// should compress build file
|
||||||
|
compress := false
|
||||||
|
if strings.ToLower(inputCompress) == "true" {
|
||||||
|
compress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each platform, execute `build` function
|
||||||
|
for _, platform := range platforms {
|
||||||
|
|
||||||
|
// split platform by `/` (and clean all whitespaces)
|
||||||
|
platformSpec := strings.Split(strings.ReplaceAll(platform, " ", ""), "/")
|
||||||
|
|
||||||
|
// create a `map` of `kernel` and `arch`
|
||||||
|
platformMap := map[string]string{
|
||||||
|
"kernel": platformSpec[0],
|
||||||
|
"arch": platformSpec[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute `build` function
|
||||||
|
build(packageName, destDir, platformMap, compress)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------*/
|
||||||
|
|
||||||
|
// list files inside destination directory
|
||||||
|
if output, err := exec.Command("ls", "-alh", destDir).Output(); err != nil {
|
||||||
|
fmt.Println("An error occurred during ls operation:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fmt.Println("--- BUILD FILES ---")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user