From fef96a0ec1d20e3cb89713fe89ffa8ce3ce30cc4 Mon Sep 17 00:00:00 2001 From: Iceflower Date: Wed, 4 Oct 2023 20:12:38 +0200 Subject: [PATCH] [ci|publisher] Update publisher --- .github/workflows/publish.yml | 15 ++- publisher/{addon_cfg.go => addon.go} | 64 +++++++------ publisher/cli.go | 132 +++++++++++++++++++++++++++ publisher/cmd_parser.go | 50 ---------- publisher/go.mod | 10 +- publisher/go.sum | 8 ++ publisher/main.go | 53 +---------- 7 files changed, 193 insertions(+), 139 deletions(-) rename publisher/{addon_cfg.go => addon.go} (52%) create mode 100644 publisher/cli.go delete mode 100644 publisher/cmd_parser.go diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7a17ae6..3fdca06 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -71,8 +71,8 @@ jobs: id: "prepare-artifacts" working-directory: publisher run: | - go run . zip ../ ${{ matrix.config.id }} - echo "version=$(go run . version ../ ${{ matrix.config.id }})" >> $GITHUB_OUTPUT + go run . zip -b ../ -a ${{ matrix.config.id }} -o ../archives/ + go run . github -b ../ -a ${{ matrix.config.id }} -o $GITHUB_OUTPUT - name: Upload Artifacts uses: actions/upload-artifact@v3 @@ -83,15 +83,15 @@ jobs: - uses: mukunku/tag-exists-action@v1.2.0 id: checkTag with: - tag: ${{ matrix.config.id }}-${{steps.prepare-artifacts.outputs.version}} + tag: ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} - name: Prepare Release if: ${{ steps.checkTag.outputs.exists == 'false' }} run: | git config user.name github-actions git config user.email github-actions@github.com - git tag ${{ matrix.config.id }}-${{steps.prepare-artifacts.outputs.version}} - git push origin tag ${{ matrix.config.id }}-${{steps.prepare-artifacts.outputs.version}} + git tag ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} + git push origin tag ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} # wait 5s that the pushed tag is available in the next step, sometimes the next step saw only the local tag - name: Wait 5s @@ -102,11 +102,8 @@ jobs: if: ${{ steps.checkTag.outputs.exists == 'false' }} env: GITHUB_TOKEN: ${{ github.token }} - NOTES: | - ${{ matrix.config.name }} - Version: ${{steps.prepare-artifacts.outputs.version}} run: | - gh release create ${{ matrix.config.id }}-${{steps.prepare-artifacts.outputs.version}} ./archives/* --title "${{ matrix.config.name }} ${{steps.prepare-artifacts.outputs.version}}" --notes "${{ env.NOTES }}" + gh release create ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} ./archives/* --title "${{ matrix.config.name }} ${{ steps.prepare-artifacts.outputs.version }}" --notes "${{ steps.prepare-artifacts.outputs.notes }}" combined-release: name: Combined Release diff --git a/publisher/addon_cfg.go b/publisher/addon.go similarity index 52% rename from publisher/addon_cfg.go rename to publisher/addon.go index c974b08..364228a 100644 --- a/publisher/addon_cfg.go +++ b/publisher/addon.go @@ -2,52 +2,65 @@ package main import ( "archive/zip" + "log" "os" "path/filepath" - "strings" "github.com/pelletier/go-toml/v2" ) -type addonConfig struct { +type pluginCfg struct { + Plugin struct { + Author string `toml:"author"` + Description string `toml:"description"` + Classifiers []string `toml:"classifiers"` + License string `toml:"license"` + Keywords []string `toml:"keywords"` + Name string `toml:"name"` + Repository string `toml:"repository"` + Script string `toml:"script"` + Version string `toml:"version"` + + Dependencies struct { + Godot string `toml:"godot"` + } `toml:"dependencies"` + } `toml:"plugin"` +} + +type addon struct { addonId string projectPath string } -func newAddonConfig(name string, projectPath string) *addonConfig { - return &addonConfig{ +func newAddon(name string, projectPath string) *addon { + return &addon{ addonId: name, projectPath: projectPath, } } -func (addon *addonConfig) Id() string { +func (addon *addon) Id() string { return addon.addonId } -func (addon *addonConfig) ProjectPath() string { +func (addon *addon) ProjectPath() string { return addon.projectPath } -func (addon *addonConfig) Path() string { +func (addon *addon) Path() string { return filepath.Join(addon.projectPath, "addons", addon.Id()) } -func (addon *addonConfig) PluginCfgPath() string { +func (addon *addon) PluginCfgPath() string { return filepath.Join(addon.Path(), "plugin.cfg") } -func (addon *addonConfig) Zip() error { - exampleDir := filepath.Join(addon.ProjectPath(), "examples", addon.Id()) - - version, err := addon.GetVersion() +func (addon *addon) Zip(outputFile string) error { + err := os.MkdirAll(filepath.Dir(outputFile), os.ModePerm) if err != nil { - return err + log.Fatalln(err) } - - destZip := filepath.Join(addon.ProjectPath(), "archives", addon.Id()+"-"+strings.ReplaceAll(version, ".", "_")+".zip") - - file, err := os.Create(destZip) + file, err := os.Create(outputFile) if err != nil { return err } @@ -61,6 +74,7 @@ func (addon *addonConfig) Zip() error { if err != nil { return err } + exampleDir := filepath.Join(addon.ProjectPath(), "examples", addon.Id()) err = zipDir(zw, exampleDir, filepath.Join("examples", addon.Id())) if err != nil { return err @@ -78,20 +92,16 @@ func (addon *addonConfig) Zip() error { return nil } -func (addon *addonConfig) GetVersion() (string, error) { +func (addon *addon) GetPluginCfg() (*pluginCfg, error) { dat, err := os.ReadFile(addon.PluginCfgPath()) if err != nil { - return "", err + return nil, err } - tmp := struct { - Plugin struct { - Version string `toml:"version"` - } `toml:"plugin"` - }{} - err = toml.Unmarshal(dat, &tmp) + tmp := &pluginCfg{} + err = toml.Unmarshal(dat, tmp) if err != nil { - return "", err + return nil, err } - return tmp.Plugin.Version, nil + return tmp, nil } diff --git a/publisher/cli.go b/publisher/cli.go new file mode 100644 index 0000000..efb4244 --- /dev/null +++ b/publisher/cli.go @@ -0,0 +1,132 @@ +package main + +import ( + "errors" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +const ( + actionGithub = "github" + actionZip = "zip" +) + +type cli struct { + rootCmd *cobra.Command + + Addon string + BaseDir string + + GithubAction struct { + OutputFile string + } + ZipAction struct { + OutputDir string + } +} + +func newCli() *cli { + c := cli{ + GithubAction: struct{ OutputFile string }{}, + ZipAction: struct{ OutputDir string }{}, + } + + c.rootCmd = &cobra.Command{ + Use: "publisher", + Short: "Kenyoni Godot Addon publishing helper", + } + c.rootCmd.PersistentFlags().StringVarP(&c.BaseDir, "baseDir", "b", "./", "Base directory of the project.") + c.rootCmd.MarkFlagRequired("baseDir") + c.rootCmd.PersistentFlags().StringVarP(&c.Addon, "addon", "a", "", "Addon to proceed.") + c.rootCmd.MarkFlagRequired("addon") + + ghCmd := &cobra.Command{ + Use: "github", + Short: "Save information about an addon to the given file, to be used with $GITHUB_OUTPUT", + Run: func(cmd *cobra.Command, args []string) { + doActionGithub(c.BaseDir, c.Addon, c.GithubAction.OutputFile) + }, + } + ghCmd.Flags().StringVarP(&c.GithubAction.OutputFile, "output", "o", "", "Output file to write the result into.") + ghCmd.MarkFlagRequired("output") + + zipCmd := &cobra.Command{ + Use: "zip", + Short: "Zip specified addon release ready.", + Run: func(cmd *cobra.Command, args []string) { + doActionZip(c.BaseDir, c.Addon, c.ZipAction.OutputDir) + }, + } + zipCmd.Flags().StringVarP(&c.ZipAction.OutputDir, "output", "o", "", "Output directory to place the archive into.") + zipCmd.MarkFlagRequired("output") + + c.rootCmd.AddCommand(ghCmd, zipCmd) + + return &c +} + +func (c *cli) Execute() { + err := c.rootCmd.Execute() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func doActionGithub(baseDir string, addon string, outputFile string) { + addonCfg := newAddon(addon, baseDir) + plg, err := addonCfg.GetPluginCfg() + if err != nil { + log.Fatalln(err) + } + outputStr := fmt.Sprintf("version=%s\n", plg.Plugin.Version) + descStr := "" + if plg.Plugin.Name != "" { + descStr += plg.Plugin.Name + " - " + plg.Plugin.Version + "\n" + } + if plg.Plugin.Description != "" { + descStr += plg.Plugin.Description + "\n" + } + if plg.Plugin.Dependencies.Godot != "" { + descStr += "Godot Compatibility: " + plg.Plugin.Dependencies.Godot + "\n" + } + outputStr += fmt.Sprintf("notes<<%s\n%s%s\n", io.EOF, descStr, io.EOF) + + oFile, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatalln(err) + } + defer oFile.Close() + _, err = oFile.WriteString(outputStr) + if err != nil { + log.Fatalln(err) + } +} + +func doActionZip(baseDir string, addon string, outputDir string) { + addonDir := filepath.Join(baseDir, "addons", addon) + _, err := os.Stat(addonDir) + if errors.Is(err, os.ErrNotExist) { + log.Fatalf("Directory '%s' does not exist", addonDir) + } + if err != nil { + log.Fatalln(err) + } + + addonCfg := newAddon(addon, baseDir) + plgCfg, err := addonCfg.GetPluginCfg() + if err != nil { + log.Fatalln(err) + } + outputFile := filepath.Join(addonCfg.ProjectPath(), "archives", addonCfg.Id()+"-"+strings.ReplaceAll(plgCfg.Plugin.Version, ".", "_")+".zip") + err = addonCfg.Zip(outputFile) + if err != nil { + log.Fatalln(err) + } +} diff --git a/publisher/cmd_parser.go b/publisher/cmd_parser.go deleted file mode 100644 index 3f7df69..0000000 --- a/publisher/cmd_parser.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "flag" - "log" - "os" -) - -const ( - actionZip = "zip" - actionVersion = "version" -) - -type cmdParser struct { - fs *flag.FlagSet - Action string - Directory string - Addons []string -} - -// Parse might exit the program -func (cp *cmdParser) Parse(arguments []string) { - err := cp.fs.Parse(arguments) - if err != nil { - log.Fatalln(err) - } - - if cp.fs.NArg() < 2 { - cp.fs.Usage() - log.Fatalln() - } - cp.Action = cp.fs.Arg(0) - if cp.Action != actionVersion && cp.Action != actionZip { - cp.fs.Usage() - log.Fatalln() - } - cp.Directory = cp.fs.Arg(1) - cp.Addons = cp.fs.Args()[2:] - if cp.Action == actionZip && len(cp.Addons) != 1 { - cp.fs.Usage() - log.Fatalln() - } -} - -func newCmdParser() *cmdParser { - cp := cmdParser{ - fs: flag.NewFlagSet(os.Args[0], flag.ExitOnError), - } - return &cp -} diff --git a/publisher/go.mod b/publisher/go.mod index 51958b5..21ace8e 100644 --- a/publisher/go.mod +++ b/publisher/go.mod @@ -2,4 +2,12 @@ module github.com/kenyoni-software/godot-addons/publisher go 1.21 -require github.com/pelletier/go-toml/v2 v2.1.0 +require ( + github.com/pelletier/go-toml/v2 v2.1.0 + github.com/spf13/cobra v1.7.0 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/publisher/go.sum b/publisher/go.sum index a1cf66d..7367744 100644 --- a/publisher/go.sum +++ b/publisher/go.sum @@ -1,10 +1,18 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/publisher/main.go b/publisher/main.go index f3411c5..06ebdfd 100644 --- a/publisher/main.go +++ b/publisher/main.go @@ -1,56 +1,5 @@ package main -import ( - "fmt" - "log" - "os" - "path/filepath" - "slices" -) - func main() { - cmdP := newCmdParser() - cmdP.Parse(os.Args[1:]) - - var err error - switch cmdP.Action { - case actionVersion: - addonCfg := newAddonConfig(cmdP.Addons[0], cmdP.Directory) - version, err := addonCfg.GetVersion() - if err != nil { - log.Fatalln(err) - } - fmt.Println(version) - case actionZip: - err = iterDirectories(cmdP.Directory, cmdP.Addons) - } - if err != nil { - log.Fatalln(err) - } -} - -func iterDirectories(baseDir string, addons []string) error { - err := os.MkdirAll(filepath.Join(baseDir, "archives"), os.ModePerm) - if err != nil { - return err - } - - dirs, err := os.ReadDir(filepath.Join(baseDir, "addons")) - if err != nil { - return err - } - for _, dir := range dirs { - if !dir.IsDir() { - continue - } - if len(addons) != 0 && !slices.Contains(addons, dir.Name()) { - continue - } - addonCfg := newAddonConfig(dir.Name(), baseDir) - err = addonCfg.Zip() - if err != nil { - return err - } - } - return nil + newCli().Execute() }