Skip to content
This repository was archived by the owner on Mar 3, 2023. It is now read-only.

Commit d23d77e

Browse files
Merge pull request #1 from asunlabs/feature/multipleEnvs
Feature/multiple envs
2 parents 87a5967 + 718b0a0 commit d23d77e

File tree

5 files changed

+153
-70
lines changed

5 files changed

+153
-70
lines changed

.env.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ AUTHOR="developerasun"
22
FOO="bar"
33
SLACK_BOT_USER_OAUTH_TOKEN="xoxb-some-value-here"
44
SLACK_CHANNEL_ID="channel-id-here"
5-
# OWLLY_DONE="true" # when update is done
5+
# OWLLY_DONE="true" // add this line when update is done

.gitignore

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Configs
22
.env
3-
config/.env.config
3+
.env.production
4+
.env.development
5+
.env.test
6+
7+
# Pre-feature
8+
src
9+
10+
config/*
11+
!config/database.go
12+
!config/keycode.go
13+
bin/*
414

515
# Binaries for programs and plugins
616
*.exe

README.md

+28-23
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,29 @@ A file-based .env change notifier for your slack team.
2020

2121
## Features
2222

23-
- Update synchronization for .env file changes
24-
- Auto-post the updated .env as attachment to slack channel
23+
- Auto-sync .env file changes
24+
- Watch multiple .env files: .env, .env.test, .env.development, .env.production
25+
- Auto-post the update to slack channel as attachment
2526
- Basic metadata supported: timestamp, .env directory
27+
- Cross platform supported: Windows, Mac OS(intel, m1 chip)
2628

2729
## Install
2830

29-
Clone this repo and compile it.
31+
Clone this repo and compile it. Go supports cross-compile.
3032

3133
```sh
3234
git clone https://github.com/asunlabs/owlly.git
33-
go build owlly.go
34-
```
3535

36-
And execute owlly.exe.
36+
# from Windows(Powershell) to Mac OS
37+
$env:GOOS = "darwin"
38+
$env:GOARCH = "amd64"
39+
go build -o ./bin/owlly-for-mac owlly.go
3740

38-
```sh
39-
./owlly.exe
41+
# for Windows
42+
go build -o ./bin/owlly-for-window.exe owlly.go
4043
```
4144

42-
Or, simply download executable from [release](https://github.com/asunlabs/owlly/releases/tag/ver0.1.0) and execute it.
45+
Or, simply download binaries from [release](https://github.com/asunlabs/owlly/releases) and execute it.
4346

4447
## Prerequisite
4548

@@ -56,41 +59,43 @@ AUTHOR="developerasun"
5659
FOO="bar"
5760
SLACK_BOT_USER_OAUTH_TOKEN="xoxb-some-value-here"
5861
SLACK_CHANNEL_ID="channel-id-here"
59-
# OWLLY_DONE="true" # when update is done
62+
# OWLLY_DONE="true" // add this line when update is done
6063
```
6164

6265
## Usage
6366

64-
1. Run Owlly to watch .env changes.
65-
66-
```sh
67-
# if you cloned a repo,
68-
go run owlly.go
69-
70-
# if you executes .exe file,
71-
./owlly.exe
72-
```
73-
7467
1. Update .env as you wish.
7568

7669
```sh
7770
FOO="bar"
7871
```
7972

80-
1. Once done, set OWLLY_DONE variable in your .env. This variable will be a key for Owlly to know if your update is done.
73+
2. Once done, set OWLLY_DONE variable in your .env. This variable will be a key for Owlly to know if your update is done.
8174

8275
```sh
8376
# length of OWLLY_DONE > 0 ? send a DM : do nothing
8477
OWLLY_DONE="true"
8578
```
8679

80+
3. Run Owlly to watch .env changes.
81+
82+
```sh
83+
# if you cloned a repo,
84+
go run owlly.go
85+
86+
# if you executes binaries,
87+
./owlly-for-windows.exe
88+
./owlly-for-mac
89+
./owlly-for-mac-m1
90+
```
91+
8792
Check your slack channel if the message is sent.
8893

8994
**Note**
9095

91-
Owlly ver 0.1.0 does not support hot-reload yet. It means that you have to re-start Owlly in the situation where 1) you sent DM already 2) but updated it again 3) and want to send it again. Log will be printed but Slack API would not invoke in the case.
96+
Owlly ver 0.1.0 does not support hot-reload yet. It means that you have to re-start Owlly in the situation where 1) you sent DM already 2) but updated it again 3) and want to send it again.
9297

93-
## Contributor
98+
## Maintainer
9499

95100
Project Created by [developerasun](https://github.com/developerasun)
96101

config/keycode.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ const (
99
Designer
1010
Welfare
1111

12-
RESERVED = "OWLLY_DONE"
12+
RESERVED = "OWLLY_DONE"
13+
OWNER_PERM = 0644
1314
)

owlly.go

+111-44
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import (
1717
)
1818

1919
var (
20+
watcher *fsnotify.Watcher
2021
api *slack.Client
2122
onlyOnce sync.Once
23+
envList = []string{".env", ".env.test", ".env.development", ".env.production"}
2224
)
2325

2426
func nilChecker(err error) {
@@ -28,14 +30,36 @@ func nilChecker(err error) {
2830
}
2931
}
3032

31-
func initWatcher() *fsnotify.Watcher {
32-
watcher, err := fsnotify.NewWatcher()
33+
func initWatcher() {
34+
_watcher, err := fsnotify.NewWatcher()
3335
nilChecker(err)
3436

35-
return watcher
37+
watcher = _watcher
3638
}
3739

40+
func getEnvList() []string {
41+
return envList
42+
}
43+
44+
// @dev start watching multiple envs
45+
func registerEnvs() {
46+
for _, v := range getEnvList() {
47+
wd, _ := os.Getwd()
48+
filePath := strings.Join([]string{wd, "\\", v}, "")
49+
50+
wErr := watcher.Add(filePath)
51+
nilChecker(wErr)
52+
color.Blue(fmt.Sprintf("watching: %v", v))
53+
}
54+
}
55+
56+
// @dev load multiple envs and init slack instance
3857
func initSlack() {
58+
for _, v := range envList {
59+
lErr := godotenv.Load(v)
60+
nilChecker(lErr)
61+
}
62+
3963
_api := slack.New(os.Getenv("SLACK_BOT_USER_OAUTH_TOKEN"))
4064
res, err := _api.AuthTest()
4165
nilChecker(err)
@@ -46,54 +70,96 @@ func initSlack() {
4670
api = _api
4771
}
4872

49-
func updateAndNotify() {
50-
bytes, readErr := os.ReadFile("./.env")
51-
nilChecker(readErr)
73+
func updateEnvs() {
74+
wd, _ := os.Getwd()
5275

53-
path, _ := os.Getwd()
54-
fullpath := strings.Join([]string{path, "/config/.env.config"}, "")
76+
for _, v := range getEnvList() {
77+
data, rErr := os.ReadFile(v)
78+
nilChecker(rErr)
5579

56-
_, statErr := os.Stat(fullpath)
80+
wrapDirName := "config"
81+
wrapDirPath := strings.Join([]string{wd, "\\", wrapDirName, "\\"}, "")
82+
wrapEnvName := strings.Join([]string{v, ".", wrapDirName}, "")
83+
wrapEnvFile := strings.Join([]string{wrapDirPath, wrapEnvName}, "")
5784

58-
if os.IsNotExist(statErr) {
59-
dirErr := os.Mkdir("/config", 0644)
60-
nilChecker(dirErr)
85+
// @dev owning user has a read and write permission: 0644
86+
os.WriteFile(wrapEnvFile, data, fs.FileMode(config.OWNER_PERM))
6187
}
88+
}
89+
90+
func cleanupEnvs() {
91+
wd, _ := os.Getwd()
92+
for _, v := range getEnvList() {
93+
fullPathForWrapEnv := strings.Join([]string{wd, "\\", "config", "\\", v, ".config"}, "")
94+
_, sErr := os.Stat(fullPathForWrapEnv)
95+
96+
if ok := os.IsExist(sErr); ok {
97+
rErr := os.Remove(fullPathForWrapEnv)
98+
nilChecker(rErr)
99+
}
62100

63-
filePermission := 0644
64-
writeErr := os.WriteFile(fullpath, bytes, fs.FileMode(filePermission))
65-
nilChecker(writeErr)
101+
_, cErr := os.Create(fullPathForWrapEnv)
102+
nilChecker(cErr)
66103

67-
// if done send a DM to slack channel
68-
if isDone := isUpdateFinished(); isDone {
69-
envString := convertEnvMapToString(fullpath)
70-
onlyOnce.Do(func() {
71-
notifyEnvChange(envString)
72-
})
104+
_data, rErr := os.ReadFile(v)
105+
nilChecker(rErr)
106+
107+
wErr := os.WriteFile(fullPathForWrapEnv, _data, fs.FileMode(config.OWNER_PERM))
108+
nilChecker(wErr)
73109
}
110+
111+
color.Red("envs config setup done")
74112
}
75113

76-
func isUpdateFinished() bool {
77-
_data, _readErr := os.ReadFile("./.env")
78-
nilChecker(_readErr)
114+
func sendSlackDM() {
115+
wd, _ := os.Getwd()
79116

80-
data := string(_data)
81-
isDone := strings.Contains(data, config.RESERVED)
117+
// has
118+
envStringMapForWrapEnv := make(map[string]string)
119+
envStringForWrapEnv := "DEFAULT_VALUE"
120+
121+
for _, v := range getEnvList() {
122+
if isDone := isUpdateFinished(); isDone[v] {
123+
fullPathForWrapEnv := strings.Join([]string{wd, "\\", "config", "\\", v, ".config"}, "")
124+
wrapEnvName := strings.Join([]string{v, ".config"}, "")
82125

83-
log.Printf(".env in root DONE value: %v", isDone)
126+
envStringForWrapEnv = convertEnvMapToString(fullPathForWrapEnv, wrapEnvName)
127+
envStringMapForWrapEnv[wrapEnvName] = envStringForWrapEnv
128+
129+
notifyEnvChange(envStringMapForWrapEnv[wrapEnvName], v)
130+
}
131+
}
132+
}
133+
134+
func isUpdateFinished() map[string]bool {
135+
// @dev initialze a map with make
136+
isDone := make(map[string]bool)
137+
138+
for _, v := range getEnvList() {
139+
wd, _ := os.Getwd()
140+
fullPath := strings.Join([]string{wd, "\\", v}, "")
141+
142+
_data, _rErr := os.ReadFile(fullPath)
143+
nilChecker(_rErr)
144+
data := string(_data)
145+
146+
hasOwllyTrigger := strings.Contains(data, config.RESERVED)
147+
isDone[v] = hasOwllyTrigger
148+
}
84149

85150
return isDone
86151
}
87152

88-
func convertEnvMapToString(envPath string) string {
153+
func convertEnvMapToString(envPath string, wrapEnvName string) string {
89154
dotenvMap, readErr := godotenv.Read(envPath)
90155
nilChecker(readErr)
91156

92157
// marshaling map: ascending sort
93158
marshalMsg, marshalErr := godotenv.Marshal(dotenvMap)
94159
nilChecker(marshalErr)
95160

96-
color.Green(marshalMsg)
161+
parsedMsg := fmt.Sprintf("file parsed: %v", wrapEnvName)
162+
color.Green(parsedMsg)
97163

98164
return marshalMsg
99165
}
@@ -109,11 +175,12 @@ func getUpdateMetadata() (string, string) {
109175
return date, dirPath // yyyy-mm-dd hh-mm
110176
}
111177

112-
func notifyEnvChange(envString string) {
178+
func notifyEnvChange(envString string, envFileName string) {
113179
date, dirPath := getUpdateMetadata()
114-
dateMsg := fmt.Sprintf(".env updated at: %v", date)
115-
pathMsg := fmt.Sprintf(".env directory is: %v", dirPath)
180+
dateMsg := fmt.Sprintf("%v updated at: %v", envFileName, date)
181+
pathMsg := fmt.Sprintf("directory is: %v", dirPath)
116182

183+
// @dev assert a parsed env value is thread-safe
117184
attachedEnv := slack.AttachmentField{
118185
Title: "Copy and paste below texts to update",
119186
Value: envString,
@@ -136,20 +203,23 @@ func notifyEnvChange(envString string) {
136203
postWhat,
137204
postAs,
138205
)
139-
206+
140207
nilChecker(msgErr)
141208

142209
resultMessage := fmt.Sprintf("message posted to channel %v at %v", channelID, date)
143210
color.Green(resultMessage)
144211
}
145212

146213
func main() {
147-
envErr := godotenv.Load(".env")
214+
cleanupEnvs()
148215

149-
nilChecker(envErr)
216+
onlyOnce.Do(func() {
217+
initSlack()
218+
initWatcher()
219+
})
150220

151-
initSlack()
152-
watcher := initWatcher()
221+
/// @dev starts monitoring the path for changes.
222+
registerEnvs()
153223

154224
go func() {
155225
for {
@@ -160,7 +230,7 @@ func main() {
160230
}
161231
if event.Has(fsnotify.Write) {
162232
log.Println("modified file: ", event.Name)
163-
updateAndNotify()
233+
updateEnvs()
164234
}
165235
case err, ok := <-watcher.Errors:
166236
nilChecker(err)
@@ -171,10 +241,7 @@ func main() {
171241
}
172242
}()
173243

174-
/// @dev starts monitoring the path for changes.
175-
watcherErr := watcher.Add(".env")
176-
nilChecker(watcherErr)
177-
178-
// Block main goroutine forever.
179-
<-make(chan struct{})
244+
sendSlackDM()
245+
color.Blue("WORK DONE! Exit Owlly 👋")
246+
os.Exit(1)
180247
}

0 commit comments

Comments
 (0)