Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
trim21 committed Jun 28, 2024
1 parent 2d1208a commit e2a5a45
Show file tree
Hide file tree
Showing 78 changed files with 6,315 additions and 0 deletions.
32 changes: 32 additions & 0 deletions args/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,35 @@ func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

import (
"strings"

"github.com/go-task/task/v3/internal/omap"
"github.com/go-task/task/v3/taskfile/ast"
)

// Parse parses command line argument: tasks and global variables
func Parse(args ...string) ([]*ast.Call, *ast.Vars) {
calls := []*ast.Call{}
globals := &ast.Vars{
OrderedMap: omap.New[string, ast.Var](),
}

for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, &ast.Call{Task: arg})
continue
}

name, value := splitVar(arg)
globals.Set(name, ast.Var{Value: value})
}

return calls, globals
}

func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}
167 changes: 167 additions & 0 deletions cmd/release/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,170 @@ func docs() error {
}
return nil
}

import (
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"time"

"github.com/Masterminds/semver/v3"
"github.com/otiai10/copy"
"github.com/spf13/pflag"
)

const (
changelogSource = "CHANGELOG.md"
changelogTarget = "website/docs/changelog.mdx"
docsSource = "website/docs"
docsTarget = "website/versioned_docs/version-latest"
)

var (
changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
versionRegex = regexp.MustCompile(`(?m)^ "version": "\d+\.\d+\.\d+",$`)
)

// Flags
var (
versionFlag bool
)

func init() {
pflag.BoolVarP(&versionFlag, "version", "v", false, "resolved version number")
pflag.Parse()
}

func main() {
if err := release(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func release() error {
if len(pflag.Args()) != 1 {
return errors.New("error: expected version number")
}

version, err := getVersion()
if err != nil {
return err
}

if err := bumpVersion(version, pflag.Arg(0)); err != nil {
return err
}

if versionFlag {
fmt.Println(version)
return nil
}

if err := changelog(version); err != nil {
return err
}

if err := setJSONVersion("package.json", version); err != nil {
return err
}

if err := setJSONVersion("package-lock.json", version); err != nil {
return err
}

if err := docs(); err != nil {
return err
}

return nil
}

func getVersion() (*semver.Version, error) {
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
b, err := cmd.Output()
if err != nil {
return nil, err
}

return semver.NewVersion(strings.TrimSpace(string(b)))
}

func bumpVersion(version *semver.Version, verb string) error {
switch verb {
case "major":
*version = version.IncMajor()
case "minor":
*version = version.IncMinor()
case "patch":
*version = version.IncPatch()
default:
*version = *semver.MustParse(verb)
}
return nil
}

func changelog(version *semver.Version) error {
// Open changelog target file
b, err := os.ReadFile(changelogTarget)
if err != nil {
return err
}

// Get the current frontmatter
currentChangelog := string(b)
sections := strings.SplitN(currentChangelog, "---", 3)
if len(sections) != 3 {
return errors.New("error: invalid frontmatter")
}
frontmatter := strings.TrimSpace(sections[1])

// Open changelog source file
b, err = os.ReadFile(changelogSource)
if err != nil {
return err
}
changelog := string(b)
date := time.Now().Format("2006-01-02")

// Replace "Unreleased" with the new version and date
changelog = changelogReleaseRegex.ReplaceAllString(changelog, fmt.Sprintf("## v%s - %s", version, date))

// Write the changelog to the source file
if err := os.WriteFile(changelogSource, []byte(changelog), 0o644); err != nil {
return err
}

// Add the frontmatter to the changelog
changelog = fmt.Sprintf("---\n%s\n---\n\n%s", frontmatter, changelog)

// Write the changelog to the target file
return os.WriteFile(changelogTarget, []byte(changelog), 0o644)
}

func setJSONVersion(fileName string, version *semver.Version) error {
// Read the JSON file
b, err := os.ReadFile(fileName)
if err != nil {
return err
}

// Replace the version
new := versionRegex.ReplaceAllString(string(b), fmt.Sprintf(` "version": "%s",`, version.String()))

// Write the JSON file
return os.WriteFile(fileName, []byte(new), 0o644)
}

func docs() error {
if err := os.RemoveAll(docsTarget); err != nil {
return err
}
if err := copy.Copy(docsSource, docsTarget); err != nil {
return err
}
return nil
}
169 changes: 169 additions & 0 deletions cmd/sleepit/sleepit.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,172 @@ func doSomeWork(deadline time.Time) bool {
time.Sleep(timeout)
return false
}

package main

import (
"flag"
"fmt"
"os"
"os/signal"
"time"
)

const usage = `sleepit: sleep for the specified duration, optionally handling signals
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
Usage: sleepit <command> [<args>]
Commands
default Use default action: on reception of SIGINT terminate abruptly
handle Handle signals: on reception of SIGINT perform cleanup before exiting
version Show the sleepit version`

// Filled by the linker.
var fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty

func main() {
os.Exit(run(os.Args[1:]))
}

func run(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, usage)
return 2
}

defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")

handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
handleTermAfter := handleCmd.Int("term-after", 0,
"Terminate immediately after `N` signals.\n"+
"Default is to terminate only when the cleanup phase has completed.")

versionCmd := flag.NewFlagSet("version", flag.ExitOnError)

switch args[0] {

case "default":
_ = defaultCmd.Parse(args[1:])
if len(defaultCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
return 2
}
return supervisor(*defaultSleep, 0, 0, nil)

case "handle":
_ = handleCmd.Parse(args[1:])
if *handleTermAfter == 1 {
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
return 2
}
if len(handleCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
return 2
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)

case "version":
_ = versionCmd.Parse(args[1:])
if len(versionCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
return 2
}
fmt.Printf("sleepit version %s\n", fullVersion)
return 0

default:
fmt.Fprintln(os.Stderr, usage)
return 2
}
}

func supervisor(
sleep time.Duration,
cleanup time.Duration,
termAfter int,
sigCh <-chan os.Signal,
) int {
fmt.Printf("sleepit: ready\n")
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
os.Getpid(), sleep, cleanup)

cancelWork := make(chan struct{})
workerDone := worker(cancelWork, sleep, "work")

cancelCleaner := make(chan struct{})
var cleanerDone <-chan struct{}

sigCount := 0
for {
select {
case sig := <-sigCh:
sigCount++
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
if sigCount == 1 {
// since `cancelWork` is unbuffered, sending will be synchronous:
// we are ensured that the worker has terminated before starting cleanup.
// This is important in some real-life situations.
cancelWork <- struct{}{}
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
}
if sigCount == termAfter {
cancelCleaner <- struct{}{}
return 4
}
case <-workerDone:
return 0
case <-cleanerDone:
return 3
}
}
}

// Start a worker goroutine and return immediately a `workerDone` channel.
// The goroutine will prepend its prints with the prefix `name`.
// The goroutine will simulate some work and will terminate when one of the following
// conditions happens:
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
// it should send a message; if instead it wants an asynchronous cancel, it should
// close the channel.
func worker(
canceled <-chan struct{},
howlong time.Duration,
name string,
) <-chan struct{} {
workerDone := make(chan struct{})
deadline := time.Now().Add(howlong)
go func() {
fmt.Printf("sleepit: %s started\n", name)
for {
select {
case <-canceled:
fmt.Printf("sleepit: %s canceled\n", name)
return
default:
if doSomeWork(deadline) {
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
workerDone <- struct{}{}
return
}
}
}
}()
return workerDone
}

// Do some work and then return, so that the caller can decide wether to continue or not.
// Return true when all work is done.
func doSomeWork(deadline time.Time) bool {
if time.Now().After(deadline) {
return true
}
timeout := 100 * time.Millisecond
time.Sleep(timeout)
return false
}
Loading

0 comments on commit e2a5a45

Please sign in to comment.