Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/ci_go.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
name: Go CI

on:
push:
branches: [ "main" ]
on: pull_request

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.21', '1.22']
go-version: ['1.24', '1.23']
steps:
- uses: actions/checkout@v3
- name: Set up Go ${{ matrix.go-version }}
Expand Down
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## v0.2.0
### Changed
- Restructured/Refactored code base and removed unnecessary intermediary components
- PHONYs will be added for every given target for now.
This will change in the future for targets that are actually files.
- New flag `debug` added for `synmake --config=<path_to_config.yaml> --debug` that
will run synmake in `debug` mode

## v0.1.1
### 😁 Features
### Features
- Print the version of synmake with `synmake version`
- Generate example config with `synmake generate config`
- Parse config and generate the Makefile with `synmake --config=<path_to_config.yaml>`
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [2024] [RaphSku]
Copyright [2025] [RaphSku]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
70 changes: 32 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
# synmake
![Version](https://img.shields.io/badge/version-v0.1.1-orange)
![Version](https://img.shields.io/badge/version-v0.2.0-orange)
![CI](https://github.com/RaphSku/synmake/workflows/Go%20CI/badge.svg)
## What is synmake?
You will not need synmake, when you have to write a simple Makefiles but synmake should rather help you in times where you cannot remember, e.g. how to setup a preflight check for checking the minimum required version of a tool or how to print a help window that describes the available targets.
You will not need synmake, when you have to write a simple Makefile but synmake should rather help you in times where you cannot remember, e.g. how to setup a preflight check for checking the minimum required version of a tool or how to print a help window that describes the available targets.

Nowadays, the majority of people are used to writing YAML. synmake allows you to write the specification in YAML and is doing the generation of the Makefile for you.

## How to install synmake
You can either install synmake via Go1.21+ with the following command:
You can install synmake with the following command:
```bash
go install github.com/RaphSku/synmake@latest
```

Alternatively, check the release page and install the binary. There are binaries provided for Windows, MacOS and Linux.

## How to use synmake
If you want to check which version you are using, just check with
To print synmake's version, use
```bash
synmake version
```
Expand All @@ -28,16 +26,15 @@ Adjust it to your needs and then you can simply generate your Makefile via
synmake --config=<path/to/your/config.yaml>
```
The Makefile will be created in the directory in which you ran this command.
If something goes wrong, you can enable debugging with
```bash
synmake --config=<path/to/your/config.yaml> --debug
```

## Understanding the config schema
The config file might look like this:
An example configuration file might look like this:
```yaml
phony:
- default
- preflight
- targetA
- targetB
- help
---
targets:
targetA:
helpDescription: targetA just prints an output
Expand All @@ -53,31 +50,28 @@ targets:
- echo "This is targetB!"
- echo "How are you doing?"
display: true
helpTemplate:
enabled: true
delimiter: '##'
versionTemplate:
enabled: true
library: example
minVersion: 0.1.0
templates:
helpTemplate:
enabled: true
delimiter: '##'
versionTemplate:
enabled: true
library: example
minVersion: 0.1.0
```
1. Phony Targets:

The phony section lists the names of phony targets (targets that are not actual files or commands) such as default, preflight, targetA, targetB, and help.

2. Targets:

Each target (targetA, targetB) has a helpDescription field providing a brief description of what the target does.
The commands field lists the commands to be executed when the target is invoked.
The display field specifies whether the commands should be displayed when running a target. If you specify `Display` as false, the commands will be shown, as well as the resulting output.
For targetB, there is a preTargets field that specifies dependencies on other targets (targetA in this case).

3. Help and Version Templates:
### Targets
Each key under targets specifies a new target where you can specify 3 attributes:
- `helpDescription` -> This field describes the target and will be added above the target as documentation. If you have set
`helpTemplate.enabled` to `true`, the `helpTemplate.delimiter` will override the default comment delimiter which is `#`.
- `commands` -> Here you can specify the commands that should be appear under the given target.
- `display` -> If `display` is `true`, the commands will be printed when you run the given target, otherwise they are
supressed by the command modifier `@`.

The helpTemplate section configures the help template:
enabled specifies whether the help template is enabled or not (true/false).
delimiter specifies the delimiter used in the help template.
The versionTemplate section configures the version template:
enabled specifies whether the version template is enabled or not (true/false).
library specifies the library or module used for checking the version.
minVersion specifies the minimum version required for the library/module.
### Templates
Templates are additional helper targets that can be optionally added to the Makefile. Currently, 2 templates are available:
- `helpTemplate` -> It adds a help target to the Makefile, such that when you run `make`, a help description is shown
with all the available targets and their help description.
- `versionTemplate` -> The version template can be used to check the version of a given tool, this is especially helpful for
preflight checks before a given target is being run. `versionTemplate.library` specifies the tool and `minVersion` the minimum
version that the tool has to satisfy.
48 changes: 28 additions & 20 deletions cmd/generate/config/config.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
package config

import (
"os"

c2 "github.com/RaphSku/synmake/internal/config"
"github.com/spf13/cobra"
"go.uber.org/zap"
"os"
)

func GetGenerateConfigCmd(logger *zap.Logger) cobra.Command {
type GenerateConfigSubCmd struct {
logger *zap.Logger
}

func NewGenerateConfigSubCmd(logger *zap.Logger) *GenerateConfigSubCmd {
return &GenerateConfigSubCmd{
logger: logger,
}
}

func (gcsc *GenerateConfigSubCmd) GetGenerateConfigSubCmd() cobra.Command {
generateConfigCmd := cobra.Command{
Use: "config",
Short: "Generate a basic config.yaml for customization.",
Long: `Generate a basic config.yaml, customize it and then apply it.`,
Run: func(cmd *cobra.Command, args []string) {
file, err := os.Create("config.yaml")
if err != nil {
logger.Error("Failed to create a config.yaml", zap.Error(err))
os.Exit(1)
}

content := "---\n"
defaultConfig := c2.GetConfigAsYamlString(logger)
content += defaultConfig

_, err = file.WriteString(content)
if err != nil {
logger.Error("Failed to write to config.yaml", zap.Error(err))
os.Exit(1)
}
},
Run: gcsc.runGenerateConfigCmd,
}

return generateConfigCmd
}

func (gcsc *GenerateConfigSubCmd) runGenerateConfigCmd(cmd *cobra.Command, args []string) {
file, err := os.OpenFile("config.yaml", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
defer file.Close()
if err != nil {
gcsc.logger.Error("Failed to create a config.yaml", zap.String("func", "runGenerateConfigCmd"), zap.Error(err))
os.Exit(1)
}
err = c2.GenerateExampleYamlConfig(gcsc.logger, file)
if err != nil {
os.Remove(file.Name())
gcsc.logger.Error("Failed to create a valid config.yaml", zap.String("func", "runGenerateConfigCmd"), zap.Error(err))
os.Exit(1)
}
}
13 changes: 8 additions & 5 deletions cmd/generate/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ import (
)

func TestIfGenerateConfigCmdIsRunning(t *testing.T) {
defer os.Remove("config.yaml")

// --- Setup Logger
logger, err := zap.NewDevelopment()
assert.NoError(t, err)

cmd := GetGenerateConfigCmd(logger)
// --- Checking whether the config.yaml is being created
generateConfigCommand := NewGenerateConfigSubCmd(logger)
cmd := generateConfigCommand.GetGenerateConfigSubCmd()

err = cmd.Execute()
assert.NoError(t, err, "Expected no error")
assert.FileExists(t, "config.yaml", "Expected file to be created")
assert.NoError(t, err)
assert.FileExists(t, "config.yaml")

shortDescription := cmd.Short
longDescription := cmd.Long
assert.Greater(t, longDescription, shortDescription)

os.Remove("config.yaml")
}
8 changes: 7 additions & 1 deletion cmd/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import (
"github.com/spf13/cobra"
)

func GetGenerateCmd() cobra.Command {
type GenerateConfigCmd struct{}

func NewGenerateConfigCmd() *GenerateConfigCmd {
return &GenerateConfigCmd{}
}

func (gcc *GenerateConfigCmd) GetGenerateConfigCmd() cobra.Command {
generateCmd := cobra.Command{
Use: "generate",
Short: "The command generate can be used to generate configuration files.",
Expand Down
8 changes: 6 additions & 2 deletions cmd/generate/generate_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package generate
package generate_test

import (
"testing"

"github.com/RaphSku/synmake/cmd/generate"
"github.com/stretchr/testify/assert"
)

func TestIfGenerateCmdIsRunning(t *testing.T) {
cmd := GetGenerateCmd()
t.Parallel()

generateCommand := generate.NewGenerateConfigCmd()
cmd := generateCommand.GetGenerateConfigCmd()

err := cmd.Execute()
assert.NoError(t, err)
Expand Down
80 changes: 54 additions & 26 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package cmd

import (
"fmt"
"os"

"github.com/RaphSku/synmake/cmd/generate"
"github.com/RaphSku/synmake/cmd/generate/config"
"github.com/RaphSku/synmake/cmd/version"
c2 "github.com/RaphSku/synmake/internal/config"
"github.com/RaphSku/synmake/internal/logging"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
Expand All @@ -17,48 +19,74 @@ type CLI struct {
rootCmd *cobra.Command
}

func NewCLI(logger *zap.Logger) *CLI {
var configFilePath string
func NewCLI() *CLI {
return &CLI{}
}

func (cli *CLI) Run() {
rootCmd := &cobra.Command{
Use: "synmake",
Short: "synmake helps you with the setup of Makefiles!",
Long: `Based on a yaml config, synmake will generate a Makefile template for you!`,
Run: func(cmd *cobra.Command, args []string) {
if configFilePath != "" {
configManager := c2.NewConfigManager(logger, configFilePath)
err := configManager.Parse().Apply()
if err != nil {
logger.Error("The config file could not be parsed and applied", zap.Error(err))
os.Exit(1)
}
}
},
Run: cli.runSynmakeCommand,
}

// --- ROOT CMD
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.Flags().StringVarP(&configFilePath, "config", "", "", "Specify the filepath to your config yaml.")
rootCmd.Flags().StringP("config", "", "", "Specify the filepath to your config yaml.")
rootCmd.MarkFlagRequired("config")
rootCmd.Flags().BoolP("debug", "", false, "Enable debug mode to get more helpful log messages.")
cli.rootCmd = rootCmd

return &CLI{
logger: logger,
rootCmd: rootCmd,
}
}

func (cli *CLI) AddSubCommands() {
versionCmd := version.GetVersionCmd(cli.logger)
// --- SUB CMD
versionCmd := version.NewVersionCmd().GetVersionCmd()
cli.rootCmd.AddCommand(versionCmd)

generateCmd := generate.GetGenerateCmd()
generateCmd := generate.NewGenerateConfigCmd().GetGenerateConfigCmd()
cli.rootCmd.AddCommand(&generateCmd)
configCmd := config.GetGenerateConfigCmd(cli.logger)

configCmd := config.NewGenerateConfigSubCmd(cli.logger).GetGenerateConfigSubCmd()
generateCmd.AddCommand(&configCmd)
}

func (cli *CLI) Execute() {
// --- EXECUTE
if err := cli.rootCmd.Execute(); err != nil {
cli.logger.Info("CLI failed to run", zap.Error(err))
fmt.Printf("CLI failed to run due to %v\n", err)
}
}

func (cli *CLI) runSynmakeCommand(cmd *cobra.Command, args []string) {
debugMode, _ := cmd.Flags().GetBool("debug")
cli.logger = logging.SetupZapLogger(debugMode)
configFilePath, _ := cmd.Flags().GetString("config")

file, err := os.OpenFile(configFilePath, os.O_RDONLY, 0644)
if err != nil {
cli.logger.Error("Could not create config file", zap.String("func", "runSynmakeCommand"), zap.Error(err))
os.Exit(1)
}
configManager, err := c2.NewConfigManager(cli.logger, file)
if err != nil {
cli.logger.Error("Failed to initialize a new config manager", zap.String("func", "runSynmakeCommand"), zap.Error(err))
os.Exit(1)
}
defer configManager.Close()

err = configManager.Parse()
if err != nil {
cli.logger.Error("Parsing of the config file failed", zap.String("func", "runSynmakeCommand"), zap.Error(err))
os.Exit(1)
}

file, err = os.OpenFile("Makefile", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
cli.logger.Error("Could not create Makefile", zap.String("func", "runSynmakeCommand"), zap.Error(err))
os.Exit(1)
}
defer file.Close()
err = configManager.Apply(file)
if err != nil {
cli.logger.Error("The config file could not be applied", zap.String("func", "runSynmakeCommand"), zap.Error(err))
os.Exit(1)
}
return
}
Loading