Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port.tcl: Offer automated switch to git-based source #157

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion src/macports1.0/macports.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,8 @@ Please edit sources.conf and change '$url' to '[string range $url 0 end-6]tarbal
# Set noninteractive mode if specified in config
if {[info exists ui_interactive] && !$ui_interactive} {
set macports::ui_options(ports_noninteractive) yes
unset -nocomplain macports::ui_options(questions_yesno) \
unset -nocomplain macports::ui_options(questions_input) \
macports::ui_options(questions_yesno) \
macports::ui_options(questions_singlechoice) \
macports::ui_options(questions_multichoice) \
macports::ui_options(questions_alternative)
Expand Down
201 changes: 201 additions & 0 deletions src/port/port.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -3927,6 +3927,161 @@ proc action_echo { action portlist opts } {
return 0
}

proc offer_clone {} {
global macports::os_platform macports::os_major macports::os_arch \
macports::sources macports::autoconf::rsync_path \
macports:rsync_options macports::getindex macports::sources_default \
macports::sudo_user macports::prefix macports::portdbpath

# CHECK IF SOURCES.CONF IS STILL DEFAULT (INDICATING THAT USER IS FIRST TIME CONTRIBUTOR)
set confpath "${macports::prefix}/etc/macports/sources.conf"
if {[catch {exec [macports::findBinary diff] "-q" $confpath "$confpath.default"} _]} {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We offer this change if sources.conf is identical with sources.conf.default or this proc has ran before without unexpectedly failing. We keep track that by creating a file ${macports::portdbpath}/contrib

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just use the cmp binary here?

set tempfd [open "${macports::portdbpath}/contrib" w]
close $tempfd
return 0
}

# OFFER GIT-BASED SOURCE AND ASK FOR PATH
set retvalue [$macports::ui_options(questions_yesno) "You do not seem have a working copy of the macports-ports git repository, which is required to contribute this change." "clone" {} {y} 0 "Create one now?"]
if {$retvalue == 1} {
# quit as user answered 'no'
set tempfd [open "${macports::portdbpath}/contrib" w]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when the user answers no, we never ask again?

close $tempfd
return 0
}

set path [$macports::ui_options(questions_input) "Where do you want to create your working copy?" [file join ~ macports-ports] 0]

# SWITCH TO SUDO_USER UID AND GID
set oldeuid [geteuid]
set oldgid [getgid]
if {[info exists macports::sudo_user]} {
set username ${macports::sudo_user}
} else {
set username [exec id -un]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will just be root, so there's probably no point?

}
set useruid [name_to_uid $username]
set usergid [uname_to_gid $username]
if {$oldeuid == 0} {
setegid $usergid
seteuid $useruid
}

# Make sure that folder is empty
set temp [file normalize $path]
if {[file exists $temp]} {
set len 0
fs-traverse _ $temp {
if {$len == 1} {
return -code error "path: $temp already exists and is not an empty directory."
}
incr len
}
}

# WALK DOWN FROM PATH TO ROOT AND ASK FOR GROUP X PERMISSIONS IF NEEDED
set paths {}
while {$temp ne [file normalize /]} {
if {![file exists $temp]} {
file mkdir $temp
}
if {[expr {[string index [file attributes $path -permissions] end] & 1}] == 0} {
lappend paths $temp
}
set temp [file dirname $temp]
}

if {[llength $paths] > 0} {
set allow [$macports::ui_options(questions_yesno) "The 'macports' user needs search access to $path to use ports in
this directory. The following directories will be changed to include 'x'
permissions for group and others:\n" "allow" $paths {y} 0 "Do you want to allow this?"]
if {$allow == 1} {
# quit as user answered 'no'
set tempfd [open "${macports::portdbpath}/contrib" w]
close $tempfd
return 0
}
foreach path $paths {
file attributes $path -permissions [expr {[file attributes $path -permissions] | 1}]
}
}

# CREATE GIT SOURCE BY CLONING MACPORTS-PORTS
# The small depth is for performance (it also implies --single-branch)
set url https://github.com/macports/macports-ports.git
if {[catch {system -nodup "[macports::findBinary git] clone --depth 1 $url $path"} result]} {
ui_debug $::errorInfo
ui_error "git clone: $result"
return 1
}

# COPY INDEXFILE FROM OLD SOURCE TO GIT SOURCE
set default_source_url [lindex $sources_default 0]
set indexfile [macports::getindex $default_source_url]

if {[catch {file copy -force $indexfile $path} error]} {
ui_debug $::errorInfo
ui_error "file copy: $error"
return -code error "Copying PortIndex to new git-based source failed"
}

# EDIT SOURCES.CONF TO USE GIT SOURCE
if {![file exists $temp]} {
return -code error "sources.conf not found in $confpath"
}

# Construct sed command
set cmdline "[macports::findBinary sed] -E s/(^rsync)/#\1/g"
if {[catch {set tmpfile [mkstemp "/tmp/sources.conf.sed.XXXXXX"]} error]} {
ui_debug $::errorInfo
ui_error "mkstemp: $error"
return -code error "mkstemp failed"
}
set tmpfd [lindex $tmpfile 0]
set tmpfile [join [lrange $tmpfile 1 end]]
set attributes [file attributes $confpath]
lappend cmdline "<${confpath}" ">@$tmpfd"
# Comment out the rsync line
if {[catch {exec -ignorestderr -- {*}$cmdline} error]} {
ui_debug $::errorInfo
ui_error "sed: $error"
file delete "$tmpfile"
close $tmpfd
return -code error "sed sed(1) failed"
}

# Write the new source
puts $tmpfd "file://[file normalize $path] \[default\]"

if {[geteuid] != [getuid]} {
seteuid $oldeuid
setegid $oldgid
}

# Replace the old sources.conf with the new one
if {[catch {file rename -force $tmpfile $confpath} error]} {
ui_debug $::errorInfo
ui_error "move: $error"
file delete "$tmpfile"
return -code error "sources.conf overwrite failed"
}

# Restore sources.conf attributes
file attributes $confpath {*}$attributes

# Update the ports tree
try {
mportsync
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need help with this one. At this point we've setup the new git source including updating sources.conf, but calling mportsync still updates the old source.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see two options:

} catch {{*} eCode eMessage} {
return -code error "Couldn't sync the ports tree: $eMessage"
}

ui_msg "Your /opt/local/etc/macports/sources.conf has been updated to use the git repository."

set tempfd [open "${macports::portdbpath}/contrib" w]
close $tempfd
return "[file dirname $indexfile] [file normalize $path]"
}
satraul marked this conversation as resolved.
Show resolved Hide resolved

proc action_portcmds { action portlist opts } {
# Operations on the port's directory and Portfile
Expand Down Expand Up @@ -3984,6 +4139,15 @@ proc action_portcmds { action portlist opts } {
array unset env *
array set env [array get boot_env]

# Offer setting up switch to git-based source for first time contributors
if {![file exists ${macports::portdbpath}/contrib]} {
# Offer clone will return the old source path and new source path. Redirect portfile to new one
set result [offer_clone]
if {[llength $result] > 1} {
regsub [lindex $result 0] $portfile [lindex $result 1] portfile
}
}

# Find an editor to edit the portfile
set editor ""
set editor_var "ports_${action}_editor"
Expand Down Expand Up @@ -5404,6 +5568,42 @@ namespace eval portclient::questions {
}
}

##
# Displays a question with prompt to input anything.
# Waits for user input indefinitely unless a timeout is specified.
# Shows the list of port passed to it without any numbers.
#
# @param msg
# The question specific message that is to be printed before asking the question.
# @param def
# The default answer to the question.
# @param timeout
# The amount of time for which a timeout is to occur.
proc ui_ask_input {msg def {timeout 0}} {
# Check for the default and print accordingly
puts -nonewline "${msg} \[$def\]: "
flush stdout

# User input (probably requires some input error checking code)
while 1 {
signal error {TERM INT}
try {
set input [gets stdin]
} catch {*} {
# An error occurred, print a newline so the error message
# doesn't occur on the prompt line and re-throw
puts ""
throw
}
signal -restart error {TERM INT}
if {$input eq ""} {
return $def
} else {
return $input
}
}
}

##
# Displays a question with 'yes' and 'no' as options.
# Waits for user input indefinitely unless a timeout is specified.
Expand Down Expand Up @@ -5717,6 +5917,7 @@ if {[isatty stdin]
&& [isatty stdout]
&& (![info exists ui_options(ports_quiet)] || $ui_options(ports_quiet) ne "yes")
&& (![info exists ui_options(ports_noninteractive)] || $ui_options(ports_noninteractive) ne "yes")} {
set ui_options(questions_input) portclient::questions::ui_ask_input
set ui_options(questions_yesno) portclient::questions::ui_ask_yesno
set ui_options(questions_singlechoice) portclient::questions::ui_ask_singlechoice
set ui_options(questions_multichoice) portclient::questions::ui_ask_multichoice
Expand Down