github.com/cs3org/reva/v2@v2.27.7/tools/prepare-release/main.go (about)

     1  // Copyright 2018-2022 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package main
    20  
    21  import (
    22  	"bytes"
    23  	"flag"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  	"os/exec"
    28  	"path"
    29  	"strings"
    30  	"time"
    31  )
    32  
    33  var (
    34  	version = flag.String("version", "", "version to release: 0.0.1")
    35  	commit  = flag.Bool("commit", false, "creates a commit")
    36  	tag     = flag.Bool("tag", false, "creates a tag")
    37  )
    38  
    39  func init() {
    40  	flag.Parse()
    41  
    42  	if *version == "" {
    43  		fmt.Fprintf(os.Stderr, "missing version: use -version flag\n")
    44  		os.Exit(1)
    45  	}
    46  }
    47  
    48  func main() {
    49  	// check if repo is dirty
    50  	if isRepoDirty() {
    51  		fmt.Fprintf(os.Stderr, "the repo is dirty, to generate a new release all changes need to be committed\n")
    52  		os.Exit(1)
    53  		return
    54  	}
    55  
    56  	// also the build is okay
    57  	cmd := exec.Command("make", "gen-doc")
    58  	run(cmd)
    59  
    60  	fmt.Printf("Generating new release: version=%s\n", *version)
    61  
    62  	dt := time.Now()
    63  	date := dt.Format("2006-01-02")
    64  	newChangelog := fmt.Sprintf("changelog/%s_%s", *version, date)
    65  
    66  	if info, _ := os.Stat("changelog/unreleased"); info == nil {
    67  		fmt.Fprintf(os.Stderr, "no changelog/unreleased folder, to create a new version you need to fill it")
    68  		os.Exit(1)
    69  	}
    70  
    71  	cmd = exec.Command("mv", "changelog/unreleased", newChangelog)
    72  	run(cmd)
    73  
    74  	// install release-deps: calens
    75  	cmd = exec.Command("make", "toolchain")
    76  	run(cmd)
    77  
    78  	// create new changelog
    79  	cmd = exec.Command("toolchain/calens", "-o", "CHANGELOG.md")
    80  	run(cmd)
    81  
    82  	// add new VERSION and BUILD_DATE
    83  	if err := os.WriteFile("VERSION", []byte(*version), 0644); err != nil {
    84  		fmt.Fprintf(os.Stderr, "error writing to VERSION file: %s", err)
    85  		os.Exit(1)
    86  	}
    87  
    88  	// add new VERSION and RELEASE_DATE
    89  	if err := os.WriteFile("RELEASE_DATE", []byte(date), 0644); err != nil {
    90  		fmt.Fprintf(os.Stderr, "error writing to RELEASE_DATE file: %s", err)
    91  		os.Exit(1)
    92  	}
    93  
    94  	tmp, err := os.MkdirTemp("", "reva-changelog")
    95  	if err != nil {
    96  		fmt.Fprintf(os.Stderr, "error creating tmp directory to store changelog: %s", err)
    97  		os.Exit(1)
    98  	}
    99  
   100  	if err := os.MkdirAll(path.Join(tmp, "changelog"), 0755); err != nil {
   101  		fmt.Fprintf(os.Stderr, "error creating changelog in temporary directory: %s", tmp)
   102  		os.RemoveAll(tmp)
   103  		os.Exit(1)
   104  	}
   105  
   106  	dir := path.Join(tmp, fmt.Sprintf("changelog/%s_%s", *version, date))
   107  	cmd = exec.Command("cp", "-a", fmt.Sprintf("changelog/%s_%s", *version, date), dir)
   108  	run(cmd)
   109  
   110  	// create new changelog
   111  	cmd = exec.Command("toolchain/calens", "-o", "changelog/NOTE.md", "-i", path.Join(tmp, "changelog"))
   112  	run(cmd)
   113  
   114  	// Generate changelog also in the documentation
   115  	if err := os.MkdirAll(fmt.Sprintf("docs/content/en/docs/changelog/%s", *version), 0755); err != nil {
   116  		fmt.Fprintf(os.Stderr, "error creating docs/content/en/docs/changelog/%s: %s", *version, err)
   117  		os.RemoveAll(tmp)
   118  		os.Exit(1)
   119  	}
   120  	os.RemoveAll(tmp)
   121  
   122  	data, err := os.ReadFile("changelog/NOTE.md")
   123  	if err != nil {
   124  		fmt.Fprintf(os.Stderr, "error reading NOTE.md: %s", err)
   125  		os.Exit(1)
   126  	}
   127  
   128  	releaseDocs := fmt.Sprintf(`
   129  ---
   130  title: "v%s"
   131  linkTitle: "v%s"
   132  weight: 40
   133  description: >
   134    Changelog for Reva v%s (%s)
   135  ---
   136  
   137  `, *version, *version, *version, date)
   138  
   139  	releaseDocs += string(data)
   140  	if err := os.WriteFile(fmt.Sprintf("docs/content/en/docs/changelog/%s/_index.md", *version), []byte(releaseDocs), 0644); err != nil {
   141  		fmt.Fprintf(os.Stderr, "error writing docs release file _index.md: %s", err)
   142  		os.Exit(1)
   143  	}
   144  
   145  	add(fmt.Sprintf("v%s", *version),
   146  		"changelog",
   147  		"CHANGELOG.md",
   148  		"VERSION",
   149  		"RELEASE_DATE",
   150  		"docs/content/en/docs/changelog",
   151  	)
   152  
   153  	if *commit {
   154  		createCommit(fmt.Sprintf("v%s", *version))
   155  		fmt.Println("Commit created, check with git log")
   156  	}
   157  
   158  	if *tag {
   159  		createTag(*version)
   160  		fmt.Println("Tag created, check with git tag")
   161  	}
   162  
   163  	if *tag && *commit {
   164  		fmt.Println("RELEASE READY: you only need to\n$ git push --follow-tags")
   165  		os.Exit(0)
   166  	} else {
   167  		fmt.Println("Was a dry run, run with -commit and -tag to create release")
   168  		os.Exit(1)
   169  	}
   170  }
   171  
   172  func isRepoDirty() bool {
   173  	repo := "."
   174  	cmd := exec.Command("git", "status", "-s")
   175  	cmd.Dir = repo
   176  	changes := runAndGet(cmd)
   177  	if changes != "" {
   178  		fmt.Println("repo is dirty")
   179  		fmt.Println(changes)
   180  	}
   181  	return changes != ""
   182  }
   183  
   184  func add(msg string, files ...string) {
   185  	for _, f := range files {
   186  		cmd := exec.Command("git", "add", "--all", f)
   187  		cmd.Dir = "."
   188  		run(cmd)
   189  	}
   190  }
   191  
   192  func createCommit(msg string) {
   193  	cmd := exec.Command("git", "commit", "-m", msg)
   194  	cmd.Dir = "." // always run from the root of the repo
   195  	run(cmd)
   196  }
   197  
   198  func createTag(version string) {
   199  	// check if repo is dirty
   200  	if isRepoDirty() {
   201  		fmt.Fprintf(os.Stderr, "repo is dirty when creating a new tag for version %s", version)
   202  		os.Exit(1)
   203  	}
   204  
   205  	cmd := exec.Command("git", "tag", "-a", "v"+version, "-m", "v"+version)
   206  	run(cmd)
   207  }
   208  
   209  func run(cmd *exec.Cmd) {
   210  	var b bytes.Buffer
   211  	mw := io.MultiWriter(os.Stdout, &b)
   212  	cmd.Stdout = mw
   213  	cmd.Stderr = mw
   214  	err := cmd.Run()
   215  	fmt.Println(cmd.Dir, cmd.Args)
   216  	fmt.Println(b.String())
   217  	if err != nil {
   218  		fmt.Println("ERROR: ", err.Error())
   219  		os.Exit(1)
   220  	}
   221  }
   222  
   223  func runAndGet(cmd *exec.Cmd) string {
   224  	var b bytes.Buffer
   225  	mw := io.MultiWriter(os.Stdout, &b)
   226  	cmd.Stderr = mw
   227  	out, err := cmd.Output()
   228  	fmt.Println(cmd.Dir, cmd.Args)
   229  	fmt.Println(b.String())
   230  	if err != nil {
   231  		fmt.Println("ERROR: ", err.Error())
   232  		os.Exit(1)
   233  	}
   234  	return strings.TrimSpace(string(out))
   235  }