Skip to content

Commit

Permalink
fix typos and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
costaluu committed Sep 14, 2024
1 parent 9ac9171 commit f1156d7
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 20 deletions.
128 changes: 126 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,129 @@
# Flag

Flag is a feature flag system at branch level
**Flag** is a branch-level feature flag manager designed to enable feature toggling at the file level. With deep integration into Git, Flag automatically adapts files in your repository based on which features are turned on or off.

Flag only works with git repositories
> [!WARNING]
> Disclaimer: This system is currently in Beta. Use with caution in production environments.
## Table of Contents

- [Overview](#overview)
- [Key Features](#key-features)
- [Blocks](#blocks)
- [Delimiters](#delimiters)
- [Commits](#commits)
- [Commands](#commands)
- [Getting Started](#getting-started)

---

## Overview

Feature flagging has become a critical tool for controlled feature deployment, but **Flag** takes this to the next level by allowing feature toggling at the **file level**. Instead of toggling functionality purely in code, Flag modifies actual file content based on active features.

By leveraging Git, Flag ensures that changes are versioned, tracked, and consistent across your codebase. Features can be managed dynamically through **blocks**, **delimiters**, and **commits**, with seamless Git integration to track every change.

---

## Key Features

- **File-based Feature Toggling**: Modify file content dynamically based on active features.
- **Git Integration**: Works only within Git repositories for version control and tracking.
- **Blocks, Delimiters, and Commits**: Three core concepts for managing features in code and configuration files.
- **Branch-based**: Feature flags are isolated at the branch level, allowing for granular control.

---

## Blocks

**Blocks** are the fundamental units in Flag, allowing you to define sections of code that can be toggled on or off based on active features.

### Structure of a Block:

```plaintext
// @feature(myFeature) //
<feature_content>
// @default(myFeature) //
<default_content>
// !feature //
```

- <feature_content>: This content is visible when the feature is enabled.
- <default_content>: This content is used when the feature is disabled.
- //: The delimiters used to enclose block definitions.

The system reads and modifies the content based on the feature's status. When you toggle a feature on, the `<feature_content>` is inserted into the file. When it's off, the `<default_content>` is used.

Use always the sync ommand to keep your features updated

---

## Delimeters

Delimiters are used to define the boundaries for blocks. They let the system recognize where feature-specific content starts and ends in a file.

You can manage delimiters with the following commands:

- Set a delimiter: `flag delimeters set <file_extension> <delimeter_start> <delimeter_end>`
- List delimiters: `flag delimeters list`
- Delete a delimiter: `flag delimeters delete <file_extension>`

These operations let you fully control how Flag identifies and processes blocks in your files.

---

## Commits

In cases where block delimiters are not allowed (such as JSON files, which do not support comments), Flag uses Commits to manage features.

### Commit Workflow:

1. Create a base commit: `flag commits base`
This creates a base reference of the files to start tracking features.
2. Sync features

## States vs. Features

- Features: Regular feature toggles.
- States: A combination of multiple features, e.g., feature1+feature2.

Flag also supports operations like updating specific features, creating new states, and deleting features.

# Commands

```
NAME:
flag - flag is a branch-level feature flag manager
USAGE:
flag [global options] command [command options]
VERSION:
v0.0.1
AUTHOR:
costaluu
COMMANDS:
init creates a new workspace
sync updates all features on created, modified, deleted files
report shows a workspace report of features
delimeters operations for delimiters
blocks operations for block features
commits operations for commit-based features
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
```

---

# Getting started

1. Install Flag: Download the last release for your OS.
2. Initialize Workspace: Run flag init to create a new workspace in your Git repository.
3. Define Feature Blocks: Use the @feature and @default block structure in your files.
4. Manage Delimiters: Set up delimiters using flag delimeters set.
5. Handle Files Without Comments: Use commits-based tracking with flag commits base and flag sync.
12 changes: 5 additions & 7 deletions src/commands/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/costaluu/flag/constants"
"github.com/costaluu/flag/core"
"github.com/costaluu/flag/logger"
"github.com/costaluu/flag/utils"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -67,20 +68,16 @@ var BlocksFeaturesDemoteCommand *cli.Command = &cli.Command{
},
}

// TODO
var BlocksFeaturesDetailsCommand *cli.Command = &cli.Command{
Name: "details",
Usage: "show a report for a file",
ArgsUsage: `<feature_name>`,
Action: func(ctx *cli.Context) error {
args := ctx.Args().Slice()
selectedItem := utils.PickAllFiles("Pick a file to show details")

if len(args) < 1 {
logger.Result[string](fmt.Sprintf("usage: %s blocks %s %s", constants.COMMAND, ctx.Command.Name, ctx.Command.ArgsUsage))
if selectedItem.ItemTitle != "" {
core.BlockDetails(selectedItem.ItemTitle)
}

core.DemoteBlockFeature(args[0])

return nil
},
}
Expand All @@ -93,5 +90,6 @@ var BlocksFeaturesCommand *cli.Command = &cli.Command{
BlocksFeaturesPromoteCommand,
BlocksFeaturesDemoteCommand,
BlocksFeaturesDetailsCommand,
BlocksFeaturesDetailsCommand,
},
}
6 changes: 3 additions & 3 deletions src/commands/commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ var CommitsFeaturesBaseCommand *cli.Command = &cli.Command{
&cli.BoolFlag{Name: "skip-form", Aliases: []string{"sf"}},
},
Action: func(ctx *cli.Context) error {
selectedItem := utils.PickModifedOrUntrackedFiles("Pick a file to make a commit base")
selectedItem := utils.PickAllFiles("Pick a file to make a commit base")

if selectedItem.ItemTitle != "" {
core.CommitBase(selectedItem.ItemTitle, ctx.Bool("skip-form"))
Expand All @@ -83,7 +83,7 @@ var CommitsFeaturesNewFeatureCommand *cli.Command = &cli.Command{
args := ctx.Args().Slice()

if len(args) < 1 {
logger.Result[string](fmt.Sprintf("usage: %s %s %s", constants.COMMAND, ctx.Command.Name, ctx.Command.ArgsUsage))
logger.Result[string](fmt.Sprintf("usage: %s commits %s %s", constants.COMMAND, ctx.Command.Name, ctx.Command.ArgsUsage))
}

if len(args[0]) < constants.MIN_FEATURE_CHARACTERS {
Expand Down Expand Up @@ -132,7 +132,7 @@ var CommitsFeaturesDetailsCommand *cli.Command = &cli.Command{
Name: "details",
Usage: "show a report for a commit base",
Action: func(ctx *cli.Context) error {
selectedItem := utils.PickModifedOrUntrackedFiles("Select the commit base to delete")
selectedItem := utils.PickAllFiles("Pick a file to show details")

if selectedItem.ItemTitle != "" {
core.CommitDetailsFromPath(selectedItem.ItemTitle)
Expand Down
4 changes: 2 additions & 2 deletions src/commands/delimeter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var DelimeterSetCommand *cli.Command = &cli.Command{
args := ctx.Args().Slice()

if len(args) < 3 {
logger.Result[string](fmt.Sprintf("usage: %s delimeter %s", constants.COMMAND, ctx.Command.ArgsUsage))
logger.Result[string](fmt.Sprintf("usage: %s delimeters %s", constants.COMMAND, ctx.Command.ArgsUsage))
}

extension := args[0]
Expand All @@ -52,7 +52,7 @@ var DelimeterDeleteCommand *cli.Command = &cli.Command{
args := ctx.Args().Slice()

if len(args) != 3 {
logger.Info[string](fmt.Sprintf("usage: %s delimeter %s", constants.COMMAND, ctx.Command.ArgsUsage))
logger.Info[string](fmt.Sprintf("usage: %s delimeters %s", constants.COMMAND, ctx.Command.ArgsUsage))

return nil
}
Expand Down
8 changes: 3 additions & 5 deletions src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ import (
"github.com/urfave/cli/v2"
)


func main() {
// core.UnSyncAllBlocksFromPath("src/base.txt")
app := &cli.App{
Name: constants.APP_NAME,
Version: constants.VERSION,
Authors: []*cli.Author{
&cli.Author{
Name: "Lucas Costa",
Name: "costaluu",
},
},
Usage: "flag is a branch-level feature flag manager",
Expand All @@ -30,8 +28,8 @@ func main() {
commands.CommitsFeaturesCommand,
},
}

if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
}
37 changes: 37 additions & 0 deletions src/utils/filepicker.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,42 @@ func PickModifedOrUntrackedFiles(title string) components.FileListItem {

result := components.FilePickerList(title, items)

return result
}

func PickAllFiles(title string) components.FileListItem {
files := FileListAllFiles()

var rootDir string = git.GetRepositoryRoot()

var items []components.FileListItem = []components.FileListItem{}

for _, path := range files {
fullPath := filepath.Join(rootDir, path)
fileInfo, err := os.Stat(fullPath)

if err != nil {
logger.Fatal[error](err)
}

sizeInBytes := fileInfo.Size()
var size string

if sizeInBytes > 1000000 {
size = fmt.Sprintf("%dmb", int64(sizeInBytes/1000000))
} else if sizeInBytes > 1000 {
size = fmt.Sprintf("%dkb", int64(sizeInBytes/1000))
} else {
size = fmt.Sprintf("%db", sizeInBytes)
}

items = append(items, components.FileListItem{
ItemTitle: path,
Desc: fmt.Sprintf("%s %s %s", fileInfo.ModTime().Format("01-02-2006 15:04:05"), fileInfo.Mode(), size),
})
}

result := components.FilePickerList(title, items)

return result
}
8 changes: 7 additions & 1 deletion src/utils/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,13 @@ func FileListAllFiles() []string {
return nil
}

files = append(files, path)
relativePath, err := filepath.Rel(rootDir, path)

if err != nil {
return err
}

files = append(files, relativePath)

return nil
})
Expand Down

0 comments on commit f1156d7

Please sign in to comment.