|
root / src / main.reef
main.reef Reef 285 lines 9.4 KB
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/******************************************************************************
               __               ____                __
              / /   ___  ____ _/ __/_____________ _/ /__
             / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
            / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
           /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

   (C)opyright 2025, Leafscale, LLC -  https://www.leafscale.com

   Project: Zygaena
  Filename: main.reef
   Authors: Chris Tusa <chris.tusa@leafscale.com>
   License: <see LICENSE file included with this source code>
Description: Entry point for Coral package manager

******************************************************************************/

import sys.args
import sys.process
import sys.env
import io.dir
import core.str
import types
import exitcodes as ec
import commands.build as cmd_build
import commands.install as cmd_install
import commands.remove as cmd_remove
import commands.upgrade as cmd_upgrade
import commands.info as cmd_info
import commands.list as cmd_list
import commands.files as cmd_files
import commands.owner as cmd_owner
import commands.search as cmd_search
import commands.depends as cmd_depends
import commands.sync as cmd_sync
import commands.outdated as cmd_outdated
import commands.repo as cmd_repo
import commands.key as cmd_key
import commands.db as cmd_db
import commands.clean as cmd_clean
import commands.ports as cmd_ports
import commands.verify as cmd_verify
import util.color
import util.priv

fn version(): string
    return "0.4.5"
end version

proc print_usage()
    println("coral " + version() + " - Package manager for Zygaena")
    println("")
    println("USAGE:")
    println("    coral <command> [options] [packages...]")
    println("")
    println("COMMANDS:")
    println("  Package Management:")
    println("    install <pkg>     Install packages")
    println("    reinstall <pkg>   Reinstall packages (same as install --force)")
    println("    remove <pkg>      Remove packages")
    println("    upgrade           Upgrade packages")
    println("")
    println("  Query Commands:")
    println("    info <pkg>        Show package info")
    println("    list              List installed packages")
    println("    files <pkg>       List package files")
    println("    owner <file>      Find file owner")
    println("    search <term>     Search packages")
    println("    depends <pkg>     Show dependencies")
    println("    outdated          Show available updates")
    println("    verify <pkg>      Verify package integrity")
    println("")
    println("  Build Commands:")
    println("    build <port>      Build package from port")
    println("    sync              Update ports tree")
    println("    clean <target>    Remove build artifacts (sources|packages|build|all)")
    println("")
    println("  System Commands:")
    println("    db <cmd>          Manage package database (init|rebuild|status)")
    println("    ports <cmd>       Port development tools (new|cache)")
    println("    repo              Manage repositories")
    println("    key               Manage signing keys")
    println("")
    println("OPTIONS:")
    println("    -h, --help        Show this help")
    println("    --version         Show version")
    println("    -y, --yes         Don't ask for confirmation")
    println("    -f, --force       Force operation")
    println("    -v, --verbose     Verbose output")
    println("    -n, --dry-run     Show what would be done")
    println("    -q, --quiet       Minimal output")
    println("    --no-color        Disable colored output")
    println("    --root <path>     Use alternative root directory")
    println("    --prefix <path>   Installation prefix (default: /usr)")
    println("    --no-chroot-scripts  Run maintainer scripts on the host, not chrooted")
    println("")
    println("EXIT CODES:")
    println("    0                 Success")
    println("    1                 General error")
    println("    2                 Usage/syntax error")
    println("    10-19             Package errors")
    println("    20-29             Build errors")
    println("    30-39             Network errors")
    println("    50-59             Permission errors")
    println("    60-69             Dependency errors")
    println("    100+              Informational (scripting)")
end print_usage

// Find the index of the command argument, skipping global flags
fn find_command_index(): int
    let argc = args.count()
    mut i = 1
    while i < argc
        let arg = args.get(i)

        // Skip flags that take a value
        if arg == "--root" or arg == "--prefix"
            i = i + 2  // Skip flag and its value
            continue
        end if

        // Skip boolean flags
        if arg == "-y" or arg == "--yes" or arg == "-f" or arg == "--force" or arg == "-v" or arg == "--verbose" or arg == "-n" or arg == "--dry-run" or arg == "-q" or arg == "--quiet" or arg == "--no-color" or arg == "--no-chroot-scripts"
            i = i + 1
            continue
        end if

        // Found the command
        return i
    end while

    return -1
end find_command_index

// Parse global options from command line arguments
fn parse_global_options(): types.GlobalOptions
    mut opts = types.new_global_options()

    opts.yes = args.has_flag("y") or args.has_flag("yes")
    opts.force = args.has_flag("f") or args.has_flag("force")
    opts.verbose = args.has_flag("v") or args.has_flag("verbose")
    opts.dry_run = args.has_flag("n") or args.has_flag("dry-run")
    opts.quiet = args.has_flag("q") or args.has_flag("quiet")
    // Check for --root option
    if args.has_flag("root")
        opts.root = args.get_flag_value("root")
    end if

    // Check for --prefix option
    if args.has_flag("prefix")
        opts.prefix = args.get_flag_value("prefix")
    end if

    // Check for --no-chroot-scripts option (valueless)
    opts.no_chroot = args.has_flag("no-chroot-scripts")

    return opts
end parse_global_options

// Check if a command is a query command (doesn't require root)
fn is_query_command(cmd: string): bool
    // Query commands that don't modify the system
    if cmd == "info"
        return true
    elif cmd == "list"
        return true
    elif cmd == "files"
        return true
    elif cmd == "owner"
        return true
    elif cmd == "search"
        return true
    elif cmd == "depends"
        return true
    elif cmd == "outdated"
        return true
    elif cmd == "verify"
        return true
    elif cmd == "build"
        // Building packages doesn't require root - only writes to build directories
        return true
    elif cmd == "--help" or cmd == "-h"
        return true
    elif cmd == "--version" or cmd == "version"
        return true
    end if
    return false
end is_query_command

fn main(): int
    let argc = args.count()

    if argc < 2
        print_usage()
        return ec.EXIT_USAGE()
    end if

    // Check for --help and --version before finding command
    // (these can appear anywhere)
    if args.has_flag("help") or args.has_flag("h")
        print_usage()
        return ec.EXIT_SUCCESS()
    end if

    if args.has_flag("version")
        println("coral " + version())
        return ec.EXIT_SUCCESS()
    end if

    // Find the actual command, skipping global flags
    let cmd_index = find_command_index()

    if cmd_index < 0
        print_usage()
        return ec.EXIT_USAGE()
    end if

    let cmd = args.get(cmd_index)

    // Parse global options and set command index
    mut opts = parse_global_options()
    opts.cmd_index = cmd_index

    // Check for root privileges if required
    if not is_query_command(cmd)
        if not priv.is_root()
            color.print_error("This command requires root privileges")
            println("Run 'coral " + cmd + "' as root or with sudo")
            return ec.EXIT_ROOT_REQUIRED()
        end if
    end if

    // Dispatch to command handlers
    // Commands return exit codes
    if cmd == "build"
        return cmd_build.execute(opts)
    elif cmd == "install"
        return cmd_install.execute(opts)
    elif cmd == "reinstall"
        // Reinstall is install with force flag enabled
        mut reinstall_opts = opts
        reinstall_opts.force = true
        return cmd_install.execute(reinstall_opts)
    elif cmd == "remove"
        return cmd_remove.execute(opts)
    elif cmd == "upgrade"
        return cmd_upgrade.execute(opts)
    elif cmd == "info"
        return cmd_info.execute(opts)
    elif cmd == "list"
        return cmd_list.execute(opts)
    elif cmd == "files"
        return cmd_files.execute(opts)
    elif cmd == "owner"
        return cmd_owner.execute(opts)
    elif cmd == "search"
        return cmd_search.execute()
    elif cmd == "depends"
        return cmd_depends.execute()
    elif cmd == "sync"
        return cmd_sync.execute()
    elif cmd == "outdated"
        return cmd_outdated.execute()
    elif cmd == "repo"
        return cmd_repo.execute()
    elif cmd == "key"
        return cmd_key.execute()
    elif cmd == "db"
        return cmd_db.execute(opts)
    elif cmd == "clean"
        return cmd_clean.execute()
    elif cmd == "ports"
        return cmd_ports.execute(opts)
    elif cmd == "verify"
        return cmd_verify.execute(opts)
    elif cmd == "version"
        println(version())
        return ec.EXIT_SUCCESS()
    else
        color.print_error("unknown command '" + cmd + "'")
        println("Run 'coral --help' for usage.")
        return ec.EXIT_USAGE()
    end if
end main