├── LICENSE ├── README.md ├── add ├── add.go ├── add_version.go ├── compare.go └── version_info.go ├── cmd ├── add.go ├── copy.go ├── load.go └── root.go ├── go.mod ├── go.sum ├── innit ├── init.go └── setup.go └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mohamedjcali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aya - Simple Version Control System 2 | 3 | Aya is a simple and easy-to-use version control system that focuses on simplicity and usability. Unlike Git, which has a myriad of commands and complex error messages, Aya offers a minimal set of commands, ensuring a fast learning curve and preventing confusing errors. Our goal is to make version control accessible and straightforward for everyone. 4 | 5 | ## Key Features 6 | 7 | - Less than 6 commands 8 | - Intuitive and user-friendly 9 | - Focuses on simplicity over functionality 10 | - Fast learning curve 11 | - Prevents complex errors like those in Git 12 | 13 | ## Example Error in Git 14 | Here's an example of a confusing error in Git that you won't encounter in Aya: 15 | ```sh 16 | ! [rejected] main -> main (fetch first) 17 | error: failed to push some refs to 'https://github.com/Mohamedjcali/aya' 18 | hint: Updates were rejected because the remote contains work that you do not 19 | hint: have locally. This is usually caused by another repository pushing to 20 | hint: the same ref. If you want to integrate the remote changes, use 21 | hint: 'git pull' before pushing again. 22 | hint: See the 'Note about fast-forwards' in 'git push --help' for details. 23 | ``` 24 | 25 | ## Installation 26 | 27 | ### Download the Code 28 | 29 | 1. Go to the [code](https://github.com/Mohamedjcali/aya) page. 30 | 2. Download the code. 31 | 3. Build the code using `go build`. 32 | 4. You will see an `aya.exe` file in that directory. 33 | 34 | ### Windows 35 | 36 | #### Add Directory to PATH: 37 | 38 | 1. Right-click on This PC or Computer on the desktop or in File Explorer. 39 | 2. Click on Properties. 40 | 3. Click on Advanced system settings. 41 | 4. Click on the Environment Variables button. 42 | 5. In the System variables section, find the Path variable, select it, and click Edit. 43 | 6. Click New and add the directory where you placed your .exe file (e.g., `C:\Tools`). 44 | 7. Click OK to close all dialog boxes. 45 | 46 | ### Linux 47 | 48 | 1. Ensure your project is built and you have the executable. If not, use the following command to build it: 49 | ```sh 50 | go build . 51 | ``` 52 | 2. Move the executable to a directory that's in your PATH. Common directories include `/usr/local/bin` or `/usr/bin`. 53 | 3. If you choose to create your own directory, you need to add it to the PATH: 54 | - Open a terminal. 55 | - Edit your shell profile file (e.g., `.bashrc`, `.bash_profile`, `.zshrc`, etc.): 56 | ```sh 57 | nano ~/.bashrc # or whichever file your shell uses 58 | ``` 59 | - Add the following line at the end of the file: 60 | ```sh 61 | export PATH=$PATH:/path/to/your/directory 62 | ``` 63 | - Save the file and reload the profile: 64 | ```sh 65 | source ~/.bashrc # or whichever file your shell uses 66 | ``` 67 | 4. Test: 68 | - Open a new terminal window and type `aya --help` to ensure it works from any location: 69 | ```sh 70 | aya -h 71 | ``` 72 | 73 | By following these steps, you'll be able to run Aya commands from anywhere on your computer, both on Windows and Linux. 74 | 75 | ## Usage 76 | 77 | ### Checking Installation 78 | 79 | To check if Aya is installed correctly, open your terminal and run: 80 | 81 | ```sh 82 | aya -h 83 | ``` 84 | If you see an error message like: 85 | 86 | 87 | ```sh 88 | 'aya' is not recognized as an internal or external command, operable program or batch file. 89 | ``` 90 | then Aya is not installed correctly. Please follow the installation instructions again. 91 | 92 | If Aya is installed correctly, you will see a help message explaining the available commands. 93 | 94 | Initializing a Project 95 | To start using Aya, you need to initialize a project. Run the following command: 96 | 97 | ```sh 98 | aya init 99 | ``` 100 | Aya will prompt you to enter the project name and basic information about the project: 101 | 102 | ```sh 103 | Please enter the project name: 104 | ``` 105 | Type the project name and press Enter. 106 | 107 | ```sh 108 | Please enter the project basic info: 109 | ``` 110 | Type a short description of the project and press Enter. 111 | 112 | Aya will create a hidden .aya folder. To view hidden folders, go to your file explorer, look at the taskbar, go to the View tab, and enable the option to show hidden folders. Aya uses this folder to store all project-related information. 113 | 114 | ## Adding a New Version 115 | To add a new version to your project, use the aya add command with the necessary flags. If you run aya add without any flags, you will get an error because certain flags are required. 116 | 117 | Here are the flags you need to use: 118 | 119 | -v : The version name. It must contain only numbers and dots (e.g., 2.0.0.1). If you enter an invalid version name (e.g., 2.0.0d), Aya will give you an error. 120 | -w : The writer's name. This flag is required. 121 | -d : The description of the version. Enclose the description in quotes. 122 | -p : The branch to save the version in. This flag is optional. If not provided, Aya will use the branch specified in the .aya-config file. 123 | Example usage: 124 | 125 | ```sh 126 | aya add -v 0.0.0.1 -w cqani -d "This is the description of the version" -p main 127 | ``` 128 | ## Loading a Version 129 | To load a version, use the aya load command. By default, Aya will load the latest saved version: 130 | 131 | ```sh 132 | aya load 133 | ``` 134 | This command loads the latest version from the .aya/refs folder. 135 | 136 | To load a specific version, use the -v flag: 137 | 138 | ```sh 139 | aya load -v 1.0.0 140 | ``` 141 | To specify a branch, use the -p flag: 142 | 143 | ```sh 144 | aya load -v 1.0.0 -p main 145 | ``` 146 | ## Copying a Repository 147 | The aya copy command works like git clone. It copies a given GitHub repository to a folder created with the same name as the repository. 148 | 149 | Example usage: 150 | 151 | ```sh 152 | aya copy https://github.com/user/repository.git 153 | ``` 154 | To specify a destination folder: 155 | ```sh 156 | aya copy https://github.com/user/repository.git destination 157 | ``` 158 | Conclusion 159 | Aya is designed to be a simple and easy-to-use version control system. By following the instructions above, you should be able to get started with Aya and manage your projects efficiently. 160 | also still aya push and pull they are in work when we finish it we going to puplish it 161 | -------------------------------------------------------------------------------- /add/add.go: -------------------------------------------------------------------------------- 1 | package add 2 | /* 3 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 4 | */ 5 | import ( 6 | "bufio" 7 | "errors" 8 | "fmt" 9 | "os" 10 | "regexp" 11 | "strings" 12 | ) 13 | 14 | var Version string 15 | var Writer string 16 | var Description string 17 | var Pranch string 18 | 19 | func AddVersion() error { 20 | // Check if the project is initialized by looking for the .aya directory 21 | ayaDir := ".aya" 22 | destDir := ".aya/versions/main" 23 | // Validate the version string to be numbers only 24 | matched, err := regexp.MatchString(`^\d+(\.\d+)*$`, Version) 25 | if err != nil { 26 | return fmt.Errorf("error validating version: %v", err) 27 | } 28 | if !matched { 29 | return errors.New("version name must be in the format x.y.z with numbers only") 30 | } 31 | if _, err := os.Stat(ayaDir); os.IsNotExist(err) { 32 | return errors.New("project is not initialized. Please run 'aya init' first") 33 | } 34 | 35 | if Pranch != "" { 36 | destDir = ".aya/versions/" + Pranch 37 | Last_pranch(Pranch) 38 | } else { 39 | destDir = Check_last_pranch(".aya-config") 40 | } 41 | err = CreateAndCopyFolder(".", destDir, Version) 42 | if err != nil { 43 | return fmt.Errorf("error saving the commit version: %v", err) 44 | } 45 | err = UpdateVersion((destDir + "/" + Version)) 46 | if err != nil { 47 | return fmt.Errorf("%v", err) 48 | } 49 | diffresult, err := CompareVersions() 50 | if err != nil { 51 | return fmt.Errorf("error for comparing the versions: %v", err) 52 | } 53 | err = version_info(diffresult, Version, Pranch, Description) 54 | if err != nil { 55 | return fmt.Errorf("error for saving version data: %v", err) 56 | } 57 | return nil 58 | } 59 | 60 | func Check_last_pranch(filePath string) string { 61 | defaultDir := ".aya/versions/main" 62 | file, err := os.Open(filePath) 63 | if err != nil { 64 | if os.IsNotExist(err) { 65 | return defaultDir 66 | } 67 | return defaultDir 68 | } 69 | defer file.Close() 70 | 71 | scanner := bufio.NewScanner(file) 72 | if scanner.Scan() { 73 | return ".aya/versions/" + strings.TrimSpace(scanner.Text()) 74 | } 75 | 76 | if err := scanner.Err(); err != nil { 77 | // handle the error here if needed 78 | return defaultDir 79 | } 80 | 81 | return defaultDir 82 | } 83 | func Last_pranch(content string) error { 84 | fileName := ".aya-config" 85 | 86 | // Open the file in read-write mode 87 | file, err := os.OpenFile(fileName, os.O_RDWR, 0644) 88 | if err != nil { 89 | return fmt.Errorf("failed to open file: %w", err) 90 | } 91 | defer file.Close() 92 | 93 | // Read the file content 94 | scanner := bufio.NewScanner(file) 95 | lines := []string{} 96 | for scanner.Scan() { 97 | lines = append(lines, scanner.Text()) 98 | } 99 | 100 | if err := scanner.Err(); err != nil { 101 | return fmt.Errorf("failed to read file: %w", err) 102 | } 103 | 104 | // Replace the first line 105 | if len(lines) > 0 { 106 | lines[0] = content 107 | } else { 108 | // If the file is empty, just add the content as the first line 109 | lines = append(lines, content) 110 | } 111 | 112 | // Move the file pointer to the beginning 113 | if _, err := file.Seek(0, 0); err != nil { 114 | return fmt.Errorf("failed to seek file: %w", err) 115 | } 116 | 117 | // Truncate the file to zero length 118 | if err := file.Truncate(0); err != nil { 119 | return fmt.Errorf("failed to truncate file: %w", err) 120 | } 121 | 122 | // Write the updated content back to the file 123 | writer := bufio.NewWriter(file) 124 | for _, line := range lines { 125 | if _, err := writer.WriteString(line + "\n"); err != nil { 126 | return fmt.Errorf("failed to write to file: %w", err) 127 | } 128 | } 129 | 130 | if err := writer.Flush(); err != nil { 131 | return fmt.Errorf("failed to flush to file: %w", err) 132 | } 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /add/add_version.go: -------------------------------------------------------------------------------- 1 | package add 2 | /* 3 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 4 | 5 | */ 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "io" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | ) 14 | 15 | func CreateAndCopyFolder(srcDir, destDir, newFolderName string) error { 16 | // Clean up paths 17 | srcDir = filepath.Clean(srcDir) 18 | destDir = filepath.Clean(destDir) 19 | 20 | // Ensure source directory exists 21 | if _, err := os.Stat(srcDir); os.IsNotExist(err) { 22 | return fmt.Errorf("source directory %s does not exist", srcDir) 23 | } 24 | 25 | // Create target directory path 26 | targetDir := filepath.Join(destDir, newFolderName) 27 | 28 | // Check if the target directory already exists 29 | if _, err := os.Stat(targetDir); !os.IsNotExist(err) { 30 | return fmt.Errorf("folder %s already exists", targetDir) 31 | } 32 | 33 | // Create the target directory 34 | if err := os.MkdirAll(targetDir, 0755); err != nil { 35 | return err 36 | } 37 | 38 | // Get list of folders to ignore 39 | ignoreList, err := getIgnoreList(filepath.Join(srcDir, ".ayaignore")) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | // Copy directory contents 45 | var copyDir func(string, string) error 46 | copyDir = func(src, dst string) error { 47 | entries, err := os.ReadDir(src) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | for _, entry := range entries { 53 | if contains(ignoreList, entry.Name()) { 54 | continue // Ignore listed folders 55 | } 56 | 57 | srcPath := filepath.Join(src, entry.Name()) 58 | dstPath := filepath.Join(dst, entry.Name()) 59 | 60 | if entry.IsDir() { 61 | if err := os.MkdirAll(dstPath, entry.Type()); err != nil { 62 | return err 63 | } 64 | if err := copyDir(srcPath, dstPath); err != nil { 65 | return err 66 | } 67 | } else { 68 | srcFile, err := os.Open(srcPath) 69 | if err != nil { 70 | return err 71 | } 72 | defer srcFile.Close() 73 | 74 | dstFile, err := os.Create(dstPath) 75 | if err != nil { 76 | return err 77 | } 78 | defer dstFile.Close() 79 | 80 | if _, err := io.Copy(dstFile, srcFile); err != nil { 81 | return err 82 | } 83 | 84 | // Copy file permissions 85 | srcInfo, err := os.Stat(srcPath) 86 | if err != nil { 87 | return err 88 | } 89 | if err := os.Chmod(dstPath, srcInfo.Mode()); err != nil { 90 | return err 91 | } 92 | } 93 | } 94 | return nil 95 | } 96 | 97 | if err := copyDir(srcDir, targetDir); err != nil { 98 | return err 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func getIgnoreList(filePath string) ([]string, error) { 105 | var ignoreList []string 106 | file, err := os.Open(filePath) 107 | if err != nil { 108 | if os.IsNotExist(err) { 109 | return ignoreList, nil // No .ayaignore file, no folders to ignore 110 | } 111 | return nil, err 112 | } 113 | defer file.Close() 114 | 115 | scanner := bufio.NewScanner(file) 116 | for scanner.Scan() { 117 | ignoreList = append(ignoreList, strings.TrimSpace(scanner.Text())) 118 | } 119 | 120 | if err := scanner.Err(); err != nil { 121 | return nil, err 122 | } 123 | 124 | return ignoreList, nil 125 | } 126 | 127 | func contains(slice []string, item string) bool { 128 | for _, s := range slice { 129 | if s == item { 130 | return true 131 | } 132 | } 133 | return false 134 | } 135 | -------------------------------------------------------------------------------- /add/compare.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | /* 4 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 5 | 6 | */ 7 | import ( 8 | "bufio" 9 | "encoding/json" 10 | "io/fs" 11 | "io/ioutil" 12 | "os" 13 | "path/filepath" 14 | ) 15 | 16 | type DiffResult struct { 17 | NewFiles []string 18 | RemovedFiles []string 19 | NewLines int 20 | RemovedLines int 21 | ChangedLines int 22 | } 23 | 24 | func CompareVersions() (DiffResult, error) { 25 | var err error 26 | filePath := ".aya/refs/aya.json" 27 | // Read the JSON file 28 | file, err := ioutil.ReadFile(filePath) 29 | if err != nil { 30 | return DiffResult{}, err 31 | } 32 | 33 | // Unmarshal the JSON into a struct 34 | var vc ProjectInfo 35 | err = json.Unmarshal(file, &vc) 36 | if err != nil { 37 | return DiffResult{}, err 38 | } 39 | oldDir := vc.LastVersion 40 | newDir := vc.NewestVersion 41 | var oldFiles, newFiles []string 42 | 43 | if oldDir != "" { 44 | oldFiles, err = listFiles(oldDir) 45 | if err != nil { 46 | return DiffResult{}, err 47 | } 48 | } 49 | 50 | if newDir != "" { 51 | newFiles, err = listFiles(newDir) 52 | if err != nil { 53 | return DiffResult{}, err 54 | } 55 | } 56 | 57 | newFilesSet := make(map[string]struct{}) 58 | for _, file := range newFiles { 59 | newFilesSet[file] = struct{}{} 60 | } 61 | 62 | oldFilesSet := make(map[string]struct{}) 63 | for _, file := range oldFiles { 64 | oldFilesSet[file] = struct{}{} 65 | } 66 | 67 | var diff DiffResult 68 | 69 | // Identify new and removed files 70 | for _, file := range newFiles { 71 | if _, exists := oldFilesSet[file]; !exists { 72 | diff.NewFiles = append(diff.NewFiles, file) 73 | newLineCount, err := countLines(filepath.Join(newDir, file)) 74 | if err != nil { 75 | return DiffResult{}, err 76 | } 77 | diff.NewLines += newLineCount 78 | } 79 | } 80 | 81 | for _, file := range oldFiles { 82 | if _, exists := newFilesSet[file]; !exists { 83 | diff.RemovedFiles = append(diff.RemovedFiles, file) 84 | removedLineCount, err := countLines(filepath.Join(oldDir, file)) 85 | if err != nil { 86 | return DiffResult{}, err 87 | } 88 | diff.RemovedLines += removedLineCount 89 | } 90 | } 91 | 92 | // Compare file contents for modified files 93 | for _, file := range newFiles { 94 | if _, exists := oldFilesSet[file]; exists { 95 | newLines, removedLines, changedLines, err := compareFiles(filepath.Join(oldDir, file), filepath.Join(newDir, file)) 96 | if err != nil { 97 | return DiffResult{}, err 98 | } 99 | diff.NewLines += newLines 100 | diff.RemovedLines += removedLines 101 | diff.ChangedLines += changedLines 102 | } 103 | } 104 | 105 | return diff, nil 106 | } 107 | 108 | func listFiles(root string) ([]string, error) { 109 | var files []string 110 | err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { 111 | if err != nil { 112 | return err 113 | } 114 | if !info.IsDir() { 115 | relativePath, err := filepath.Rel(root, path) 116 | if err != nil { 117 | return err 118 | } 119 | files = append(files, relativePath) 120 | } 121 | return nil 122 | }) 123 | return files, err 124 | } 125 | 126 | func compareFiles(oldFile, newFile string) (int, int, int, error) { 127 | oldLines, err := readLines(oldFile) 128 | if err != nil { 129 | return 0, 0, 0, err 130 | } 131 | 132 | newLines, err := readLines(newFile) 133 | if err != nil { 134 | return 0, 0, 0, err 135 | } 136 | 137 | oldLinesSet := make(map[string]int) 138 | for idx, line := range oldLines { 139 | oldLinesSet[line] = idx 140 | } 141 | 142 | newLinesSet := make(map[string]int) 143 | for idx, line := range newLines { 144 | newLinesSet[line] = idx 145 | } 146 | 147 | var newLineCount, removedLineCount, changedLineCount int 148 | 149 | // Count new and removed lines 150 | for idx, line := range newLines { 151 | if oldIdx, exists := oldLinesSet[line]; !exists { 152 | newLineCount++ 153 | } else if oldIdx != idx { 154 | changedLineCount++ 155 | } 156 | } 157 | 158 | for line := range oldLinesSet { 159 | if _, exists := newLinesSet[line]; !exists { 160 | removedLineCount++ 161 | } 162 | } 163 | 164 | return newLineCount, removedLineCount, changedLineCount, nil 165 | } 166 | 167 | func readLines(filePath string) ([]string, error) { 168 | file, err := os.Open(filePath) 169 | if err != nil { 170 | return nil, err 171 | } 172 | defer file.Close() 173 | 174 | var lines []string 175 | scanner := bufio.NewScanner(file) 176 | for scanner.Scan() { 177 | lines = append(lines, scanner.Text()) 178 | } 179 | return lines, scanner.Err() 180 | } 181 | 182 | func countLines(filePath string) (int, error) { 183 | lines, err := readLines(filePath) 184 | if err != nil { 185 | return 0, err 186 | } 187 | return len(lines), nil 188 | } 189 | -------------------------------------------------------------------------------- /add/version_info.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | /* 4 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 5 | 6 | */ 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | "time" 14 | ) 15 | 16 | type Version_info struct { 17 | VersionName string `json:"Name"` 18 | DateCreated time.Time `json:"dateCreated"` 19 | Pranch string `json:"Pranch"` 20 | VersionInfo string `json:"description"` 21 | NewFiles []string `json:"NewFiles"` 22 | RemovedFiles []string `json:"RemovedFiles"` 23 | NewLines int `json:"NewLines"` 24 | RemovedLines int `json:"RemovedLines"` 25 | ChangedLines int `json:"ChangedLines"` 26 | } 27 | type ProjectInfo struct { 28 | ProjectName string `json:"Name"` 29 | DateCreated time.Time `json:"dateCreated"` 30 | LastVersion string `json:"lastVersion"` 31 | NewestVersion string `json:"newestVersion"` 32 | LastUpdated time.Time `json:"lastUpdated"` 33 | BasicInfo string `json:"description"` 34 | } 35 | func version_info(version DiffResult, version_name string, pranch string, info string) error { 36 | version_data := Version_info{ 37 | VersionName: version_name, 38 | DateCreated: time.Now(), 39 | Pranch: pranch, 40 | VersionInfo: info, 41 | NewFiles: version.NewFiles, 42 | RemovedFiles: version.RemovedFiles, 43 | NewLines: version.NewLines, 44 | RemovedLines: version.RemovedLines, 45 | ChangedLines: version.ChangedLines, 46 | } 47 | folderPath := (".aya/refs/" + pranch) 48 | projectJSON, err := json.MarshalIndent(version_data, "", " ") 49 | if err != nil { 50 | return fmt.Errorf("error marshalling to JSON: %w", err) 51 | } 52 | 53 | // Create the file path 54 | if _, err := os.Stat(folderPath); os.IsNotExist(err) { 55 | err := os.MkdirAll(folderPath, os.ModePerm) 56 | if err != nil { 57 | return fmt.Errorf("error creating directory: %w", err) 58 | } 59 | } 60 | 61 | // Join the folder path and version name to create the file path 62 | filePath := filepath.Join(folderPath, version_name+".json") 63 | 64 | // Create the JSON file 65 | file, err := os.Create(filePath) 66 | if err != nil { 67 | return fmt.Errorf("error creating file: %w", err) 68 | } 69 | defer file.Close() 70 | 71 | // Write JSON data to the file 72 | _, err = file.Write(projectJSON) 73 | if err != nil { 74 | return fmt.Errorf("error writing to file: %w", err) 75 | } 76 | 77 | return nil 78 | } 79 | func UpdateVersion(newestVersion string) error { 80 | filePath := ".aya/refs/aya.json" 81 | // Read the JSON file 82 | file, err := ioutil.ReadFile(filePath) 83 | if err != nil { 84 | return fmt.Errorf("failed to read file: %w", err) 85 | } 86 | 87 | // Unmarshal the JSON into a struct 88 | var vc ProjectInfo 89 | err = json.Unmarshal(file, &vc) 90 | if err != nil { 91 | return fmt.Errorf("failed to unmarshal JSON: %w", err) 92 | } 93 | 94 | // Update the fields 95 | vc.LastVersion = vc.NewestVersion 96 | vc.NewestVersion = newestVersion 97 | 98 | // Marshal the struct back to JSON 99 | updatedFile, err := json.MarshalIndent(vc, "", " ") 100 | if err != nil { 101 | return fmt.Errorf("failed to marshal JSON: %w", err) 102 | } 103 | 104 | // Write the updated JSON back to the file 105 | err = ioutil.WriteFile(filePath, updatedFile, os.ModePerm) 106 | if err != nil { 107 | return fmt.Errorf("failed to write file: %w", err) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /cmd/add.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | /* 4 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 5 | 6 | */ 7 | import ( 8 | "github.com/spf13/cobra" 9 | "aya/add" 10 | ) 11 | 12 | // addCmd represents the add command 13 | var addCmd = &cobra.Command{ 14 | Use: "add", 15 | Short: "Add a new version to the version control system", 16 | Long: `This command adds a new version to the version control system with a version name, author, and description.`, 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | return add.AddVersion() 19 | }, 20 | } 21 | 22 | 23 | 24 | func init() { 25 | rootCmd.AddCommand(addCmd) 26 | 27 | // Define flags 28 | addCmd.Flags().StringVarP(&add.Version, "version", "v", "", "Version name (numbers only)") 29 | addCmd.Flags().StringVarP(&add.Writer, "writer", "w", "", "Name of the writer") 30 | addCmd.Flags().StringVarP(&add.Description, "description", "d", "", "Description of the version") 31 | addCmd.Flags().StringVarP(&add.Pranch, "pranch", "p", "", "which pranch to save ") 32 | 33 | // Mark flags as required 34 | addCmd.MarkFlagRequired("version") 35 | addCmd.MarkFlagRequired("writer") 36 | addCmd.MarkFlagRequired("description") 37 | } 38 | -------------------------------------------------------------------------------- /cmd/copy.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | /* 3 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 4 | 5 | */ 6 | import ( 7 | "archive/zip" 8 | "fmt" 9 | "github.com/schollz/progressbar/v3" 10 | "io" 11 | "net/http" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | "github.com/spf13/cobra" 16 | ) 17 | 18 | var copyCmd = &cobra.Command{ 19 | Use: "copy [url] [destination]", 20 | Short: "Copies a GitHub repository", 21 | Args: cobra.MinimumNArgs(1), 22 | Run: func(cmd *cobra.Command, args []string) { 23 | url := args[0] 24 | destination := "" 25 | 26 | if len(args) > 1 { 27 | destination = args[1] 28 | } else { 29 | destination = extractRepoName(url) 30 | } 31 | 32 | if err := cloneRepo(url, destination); err != nil { 33 | fmt.Printf("Failed to copy repository: %v\n", err) 34 | } 35 | }, 36 | } 37 | 38 | func init() { 39 | rootCmd.AddCommand(copyCmd) 40 | } 41 | 42 | func extractRepoName(url string) string { 43 | parts := strings.Split(url, "/") 44 | repoName := parts[len(parts)-1] 45 | repoName = strings.TrimSuffix(repoName, ".git") 46 | return repoName 47 | } 48 | 49 | func cloneRepo(url, destination string) error { 50 | // Convert GitHub repo URL to zip download URL 51 | zipURL := convertToZipURL(url) 52 | 53 | // Download the zip file 54 | zipFilePath, err := downloadZip(zipURL) 55 | if err != nil { 56 | return err 57 | } 58 | defer os.Remove(zipFilePath) 59 | 60 | // Unzip the file 61 | if err := unzip(zipFilePath, destination); err != nil { 62 | return err 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func convertToZipURL(url string) string { 69 | url = strings.TrimSuffix(url, ".git") 70 | return url + "/archive/refs/heads/main.zip" 71 | } 72 | 73 | func downloadZip(zipURL string) (string, error) { 74 | // Create the file 75 | out, err := os.CreateTemp("", "repo-*.zip") 76 | if err != nil { 77 | return "", err 78 | } 79 | defer out.Close() 80 | 81 | // Get the data 82 | resp, err := http.Get(zipURL) 83 | if err != nil { 84 | return "", err 85 | } 86 | defer resp.Body.Close() 87 | 88 | // Create progress bar 89 | bar := progressbar.DefaultBytes( 90 | resp.ContentLength, 91 | "downloading", 92 | ) 93 | 94 | // Write the body to file 95 | _, err = io.Copy(io.MultiWriter(out, bar), resp.Body) 96 | if err != nil { 97 | return "", err 98 | } 99 | 100 | return out.Name(), nil 101 | } 102 | 103 | func unzip(src string, dest string) error { 104 | r, err := zip.OpenReader(src) 105 | if err != nil { 106 | return err 107 | } 108 | defer r.Close() 109 | 110 | os.MkdirAll(dest, 0755) 111 | 112 | for _, f := range r.File { 113 | fpath := filepath.Join(dest, f.Name) 114 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { 115 | return fmt.Errorf("illegal file path: %s", fpath) 116 | } 117 | 118 | if f.FileInfo().IsDir() { 119 | os.MkdirAll(fpath, f.Mode()) 120 | continue 121 | } 122 | 123 | if err := os.MkdirAll(filepath.Dir(fpath), f.Mode()); err != nil { 124 | return err 125 | } 126 | 127 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | rc, err := f.Open() 133 | if err != nil { 134 | return err 135 | } 136 | 137 | _, err = io.Copy(outFile, rc) 138 | 139 | outFile.Close() 140 | rc.Close() 141 | 142 | if err != nil { 143 | return err 144 | } 145 | } 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /cmd/load.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "os" 13 | "path/filepath" 14 | "regexp" 15 | 16 | "aya/add" 17 | 18 | "github.com/spf13/cobra" 19 | ) 20 | 21 | // loadCmd represents the load command 22 | var loadCmd = &cobra.Command{ 23 | Use: "load", 24 | Short: "he loads already saed versions", 25 | Long: `this comand he is going to load already saved versions from .aya/versions folder to current directory 26 | he coping everything that is there into here`, 27 | Run: func(cmd *cobra.Command, args []string) { 28 | err := load() 29 | if err != nil { 30 | return 31 | } else { 32 | fmt.Println("we loaded the version succesfully") 33 | } 34 | }, 35 | } 36 | var LoadVersion string 37 | var LoadPranch string 38 | 39 | func init() { 40 | rootCmd.AddCommand(loadCmd) 41 | 42 | loadCmd.Flags().StringVarP(&LoadVersion, "version", "v", "", "enter the version to load") 43 | loadCmd.Flags().StringVarP(&LoadPranch, "pranch", "p", "", "enter the pranch to load from") 44 | } 45 | 46 | func load() error { 47 | ayaDir := ".aya" 48 | destDir := ".aya/versions/main" 49 | if _, err := os.Stat(ayaDir); os.IsNotExist(err) { 50 | return errors.New("project is not initialized. Please run 'aya init' then save versions to load") 51 | } 52 | filePath := ".aya/refs/aya.json" 53 | // Read the JSON file 54 | file, err := ioutil.ReadFile(filePath) 55 | if err != nil { 56 | fmt.Println("Error:", err) 57 | } 58 | 59 | // Unmarshal the JSON into a struct 60 | var vc add.ProjectInfo 61 | err = json.Unmarshal(file, &vc) 62 | if err != nil { 63 | return err 64 | } 65 | // Validate the version string to be numbers only 66 | if LoadVersion != "" { 67 | matched, err := regexp.MatchString(`^\d+(\.\d+)*$`, LoadVersion) 68 | if err != nil { 69 | return fmt.Errorf("error validating version: %v", err) 70 | } 71 | if !matched { 72 | return errors.New("version name must be in the format x.y.z with numbers only") 73 | } 74 | } 75 | if LoadPranch != "" { 76 | destDir = ".aya/versions/" + LoadPranch + "/" + LoadVersion 77 | add.Last_pranch(LoadPranch) 78 | } else { 79 | destDir = add.Check_last_pranch(".aya-config") + "/" + LoadVersion 80 | } 81 | if LoadVersion == "" && LoadPranch == "" { 82 | destDir = vc.LastVersion 83 | } 84 | 85 | if LoadVersion == "" { 86 | destDir = vc.LastVersion 87 | } 88 | fmt.Println(destDir) 89 | var answer string 90 | fmt.Println("are you sure to load(y,n):",LoadVersion) 91 | fmt.Scan(&answer) 92 | if answer == "y" || answer == "yes"{ 93 | err = cleanAndCopy(destDir, ".") 94 | if err != nil { 95 | return err 96 | } 97 | }else { 98 | fmt.Println("you canceled loading") 99 | return nil 100 | } 101 | 102 | return nil 103 | } 104 | func cleanAndCopy(destDir, targetDir string) error { 105 | // Delete contents of targetDir 106 | err := os.RemoveAll(targetDir) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | // Recreate the targetDir 112 | err = os.MkdirAll(targetDir, os.ModePerm) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | // Copy contents of destDir to targetDir 118 | return copyDir(destDir, targetDir) 119 | } 120 | 121 | func copyDir(srcDir, destDir string) error { 122 | return filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error { 123 | if err != nil { 124 | return err 125 | } 126 | 127 | relPath, err := filepath.Rel(srcDir, path) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | destPath := filepath.Join(destDir, relPath) 133 | 134 | if info.IsDir() { 135 | return os.MkdirAll(destPath, info.Mode()) 136 | } 137 | 138 | return copyFile(path, destPath) 139 | }) 140 | } 141 | 142 | func copyFile(srcFile, destFile string) error { 143 | src, err := os.Open(srcFile) 144 | if err != nil { 145 | return err 146 | } 147 | defer src.Close() 148 | 149 | srcFileInfo, err := src.Stat() 150 | if err != nil { 151 | return err 152 | } 153 | 154 | dest, err := os.Create(destFile) 155 | if err != nil { 156 | return err 157 | } 158 | defer dest.Close() 159 | 160 | _, err = io.Copy(dest, src) 161 | if err != nil { 162 | return err 163 | } 164 | 165 | return os.Chmod(destFile, srcFileInfo.Mode()) 166 | } 167 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "aya/innit" 8 | "os" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // rootCmd represents the base command when called without any subcommands 14 | var rootCmd = &cobra.Command{ 15 | Use: "aya", 16 | Short: "A minimalist version control system with fewer than 6 commands, offering a straightforward alternative to Git.", 17 | Long: `This is an open-source version control system focused on simplicity. With fewer than 6 commands, 18 | it offers a minimalistic and easy-to-use alternative to Git. Perfect for those who prefer 19 | a straightforward tool without the complexity of numerous commands. Ideal for both beginners and 20 | experienced developers who want a simpler way to manage their projects`, 21 | } 22 | func Execute() { 23 | err := rootCmd.Execute() 24 | if err != nil { 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func init() { 30 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 31 | rootCmd.AddCommand(innit.InitCmd) 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module aya 2 | 3 | go 1.21.6 4 | 5 | require ( 6 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 7 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 8 | github.com/rivo/uniseg v0.4.7 // indirect 9 | github.com/schollz/progressbar/v3 v3.14.4 // indirect 10 | github.com/spf13/cobra v1.8.1 // indirect 11 | github.com/spf13/pflag v1.0.5 // indirect 12 | golang.org/x/sys v0.21.0 // indirect 13 | golang.org/x/term v0.21.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 5 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 6 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 7 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 8 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 9 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 11 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 12 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 13 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 14 | github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9dEMrw0r74= 15 | github.com/schollz/progressbar/v3 v3.14.4/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI= 16 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 17 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 18 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 19 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 22 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 23 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 24 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 25 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 26 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 27 | golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= 28 | golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | -------------------------------------------------------------------------------- /innit/init.go: -------------------------------------------------------------------------------- 1 | package innit 2 | 3 | /* 4 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 5 | */ 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "runtime" 13 | 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | // initCmd represents the init command 18 | var InitCmd = &cobra.Command{ 19 | Use: "init", 20 | Short: "Initialize a new version control system", 21 | Long: `This command initializes a new version control system by creating a .aya directory.`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | initProject() 24 | }, 25 | } 26 | var name string 27 | var BasicInfo string 28 | 29 | func initProject() { 30 | 31 | // Define the .aya directory 32 | ayaDir := ".aya" 33 | fmt.Println("please enter the project name: ") 34 | fmt.Scan(&name) 35 | 36 | // Clear the buffer 37 | reader := bufio.NewReader(os.Stdin) 38 | reader.ReadString('\n') 39 | 40 | fmt.Println("please enter the project basic info:") 41 | BasicInfo, _ = reader.ReadString('\n') 42 | BasicInfo = BasicInfo[:len(BasicInfo)-1] // Remove the newline character 43 | 44 | fmt.Println("Project Name:", name) 45 | fmt.Println("Project Basic Info:", BasicInfo) 46 | // Check if the .aya directory already exists 47 | if _, err := os.Stat(ayaDir); !os.IsNotExist(err) { 48 | fmt.Println("Project is already initialized.") 49 | return 50 | } 51 | // Create the .aya directory 52 | err := os.Mkdir(ayaDir, 0755) 53 | if err != nil { 54 | fmt.Printf("Failed to create directory %s: %v\n", ayaDir, err) 55 | return 56 | } 57 | 58 | // Make the .aya directory hidden on Windows 59 | if runtime.GOOS == "windows" { 60 | err := exec.Command("attrib", "+H", ayaDir).Run() 61 | if err != nil { 62 | fmt.Printf("Failed to hide directory %s: %v\n", ayaDir, err) 63 | return 64 | } 65 | } 66 | 67 | // Create necessary files and directories inside the .aya directory 68 | files := []string{ 69 | "HEAD", 70 | "config", 71 | "objects", 72 | "refs", 73 | "versions", 74 | } 75 | 76 | for _, file := range files { 77 | path := filepath.Join(ayaDir, file) 78 | var fileMode os.FileMode 79 | if file == "objects" || file == "refs" || file == "versions" { 80 | err = os.Mkdir(path, 0755) 81 | } else { 82 | fileMode = 0644 83 | _, err = os.Create(path) 84 | } 85 | if err != nil { 86 | fmt.Printf("Failed to create %s: %v\n", path, err) 87 | return 88 | } 89 | if fileMode != 0 { 90 | err = os.Chmod(path, fileMode) 91 | if err != nil { 92 | fmt.Printf("Failed to set permissions for %s: %v\n", path, err) 93 | } 94 | } 95 | } 96 | err = setup(name, BasicInfo, ".aya/refs") 97 | if err != nil { 98 | fmt.Println("Error creating project JSON:", err) 99 | } 100 | // Create .ayaignore file 101 | create_ignore_file() 102 | // Create .aya-config file 103 | create_config_file() 104 | fmt.Println("Initialized empty Aya repository in", ayaDir) 105 | } 106 | func create_ignore_file() { 107 | ayaIgnoreFile := filepath.Join(".", ".ayaignore") 108 | 109 | // Create the .ayaignore file 110 | file, err := os.Create(ayaIgnoreFile) 111 | if err != nil { 112 | fmt.Printf("Failed to create %s: %v\n", ayaIgnoreFile, err) 113 | return 114 | } 115 | defer file.Close() 116 | 117 | // Content to be written into the .ayaignore file 118 | content := ` 119 | .aya 120 | go.mod 121 | go.sum 122 | .ayaignore 123 | .ayaconfig 124 | node_modules` 125 | 126 | // Write the content to the file 127 | _, err = file.WriteString(content) 128 | if err != nil { 129 | fmt.Printf("Failed to write to %s: %v\n", ayaIgnoreFile, err) 130 | return 131 | } 132 | } 133 | func create_config_file() { 134 | ayaconfigFile := filepath.Join(".", ".aya-config") 135 | 136 | // Create the .aya-config file 137 | file, err := os.Create(ayaconfigFile) 138 | if err != nil { 139 | fmt.Printf("Failed to create %s: %v\n", ayaconfigFile, err) 140 | return 141 | } 142 | defer file.Close() 143 | 144 | // Content to be written into the .aya-config file 145 | content := `main` 146 | 147 | // Write the content to the file 148 | _, err = file.WriteString(content) 149 | if err != nil { 150 | fmt.Printf("Failed to write to %s: %v\n", ayaconfigFile, err) 151 | return 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /innit/setup.go: -------------------------------------------------------------------------------- 1 | package innit 2 | 3 | /* 4 | Copyright © 2024 cqani HERE cqanimohamed4@gmail.com 5 | 6 | */ 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | "time" 13 | ) 14 | 15 | type ProjectInfo struct { 16 | ProjectName string `json:"Name"` 17 | DateCreated time.Time `json:"dateCreated"` 18 | LastVersion string `json:"lastVersion"` 19 | NewestVersion string `json:"newestVersion"` 20 | LastUpdated time.Time `json:"lastUpdated"` 21 | BasicInfo string `json:"description"` 22 | } 23 | 24 | func setup(name string, discription string, folderPath string) error { 25 | project := ProjectInfo{ 26 | ProjectName: name, 27 | DateCreated: time.Now(), 28 | NewestVersion: "1.0", 29 | LastVersion: "0.0", 30 | LastUpdated: time.Now(), 31 | BasicInfo: discription, 32 | } 33 | projectJSON, err := json.MarshalIndent(project, "", " ") 34 | if err != nil { 35 | return fmt.Errorf("error marshalling to JSON: %w", err) 36 | } 37 | 38 | // Create the file path 39 | filePath := filepath.Join(folderPath, "aya.json") 40 | 41 | // Create the JSON file 42 | file, err := os.Create(filePath) 43 | if err != nil { 44 | return fmt.Errorf("error creating file: %w", err) 45 | } 46 | defer file.Close() 47 | 48 | // Write JSON data to the file 49 | _, err = file.Write(projectJSON) 50 | if err != nil { 51 | return fmt.Errorf("error writing to file: %w", err) 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2024 NAME HERE 3 | */ 4 | package main 5 | 6 | import ( 7 | "aya/cmd" 8 | ) 9 | 10 | func main() { 11 | cmd.Execute() 12 | } 13 | --------------------------------------------------------------------------------