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

project sync executed in project root directory #86

Merged
merged 14 commits into from Oct 25, 2017

Conversation

grayside
Copy link
Contributor

@grayside grayside commented Oct 14, 2017

Corrects the assumption in project_sync.go that commands should be executed from the current working directory. Instead, commands are executed the same as rig project scripts -- from the directory in which the configuration file is found.

  • If no project config is found, using current working directory as the local side of the file sync, per existing shipped behavior.
  • RemoveLogFile resolution: inline the logic or make a stand-alone API function.

Fixes #67

@grayside
Copy link
Contributor Author

This needs more testing for execution when there isn't any project configuration.

@grayside
Copy link
Contributor Author

The change as implemented removes support for the current working directory as a fallback to their being no configuration files available, so that needs to be put back.

@febbraro
Copy link
Member

So you are saying that before this PR the root for the sync dir was whatever dir you were in when you ran the command, and not the directory that the outrigger.yml file is in?

Now with this PR it will only work if there is an outrigger.yml and use that file's directory, but we also need to account for the current working directory if that project config file doesn't exist?

Also, with the issue you linked, recursive project config discovery, that seems kind of problematic. Maybe the project config file need to specify the sync dir, and if there is no project config in the recursive heirarchy that specifies a sync dir then it just uses the current dir?

@grayside
Copy link
Contributor Author

So you are saying that before this PR the root for the sync dir was whatever dir you were in when you ran the command, and not the directory that the outrigger.yml file is in?

Correct, if you look at the code, it used os.Getwd() pretty ubiquitously.

Now with this PR it will only work if there is an outrigger.yml and use that file's directory, but we also need to account for the current working directory if that project config file doesn't exist?

Yes, that's what my latest comment discussed. I over-corrected. We wanted to be able to use this functionality outside a project context. That seems like an edge case (similar to us using docker run instead of docker-compose run), but still probably worth supporting for quick experiments.

Also, with the issue you linked, recursive project config discovery, that seems kind of problematic. Maybe the project config file need to specify the sync dir, and if there is no project config in the recursive heirarchy that specifies a sync dir then it just uses the current dir?

The idea is that if an outrigger.yml is found, the rig project sync should be rooted in that directory. If not, it should be the current working directory. It makes sense to me that we further enhance the outrigger.yml unison config to specify a relative path to sync from below the project config structure. Currently all rig project scripts execute in the directory of the outrigger.yml file, we assume it is always at the repo root.

@grayside grayside changed the title project sync executed in project root directory. project sync executed in project root directory [WIP] Oct 18, 2017
@febbraro
Copy link
Member

But it is possibly to have a "global" outrigger.yml, say in your $HOME dir, and it is possible without a project config (for various use cases) that it will share your $HOME dir via unison. Should we additionally add a flag that says use $CWD?

@grayside
Copy link
Contributor Author

I think it makes sense to add a flag to designate the localhost side of the sync as a path. So --sync-from=. or --sync-from=$PWD would end up as the current directory, but other options are available, such as if a particular project is structured such that only the contents of a subdirectory should be synced.

In priority order, this gives us:

  1. --sync-from flag
  2. outrigger.yml
  3. Current working directory

As long as we're talking about these options, it's worth noting that we currently have an argument that causes problems for other commands to interact with the sync container, so we should be careful to keep those use cases in mind. #85 for discussion on that.

@febbraro
Copy link
Member

Would --sync-path or --sync-root work better?

@grayside
Copy link
Contributor Author

Re-introduced the CWD fallback, added --sync-path override, added verbose logging of what the working directory is for sync execution, followed that through to do similar in project scripts and tweaked a couple log messages for easier reading flow.

@grayside grayside changed the title project sync executed in project root directory [WIP] project sync executed in project root directory Oct 19, 2017
// process is still watching the "current directory" and so needs the working
// directory to be explicitly set.
command.Dir = workingDir
cmd.out.Verbose.Printf("Script execution - Working Directory: %s", workingDir)
Copy link
Member

Choose a reason for hiding this comment

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

Log message appears wrong. This should be Sync or something besides Script execution

@grayside
Copy link
Contributor Author

  • Log message appears wrong. Script execution -> Sync execution

cmd.out.Info.Print("Waiting for initial sync detection")

var tempFile = fmt.Sprintf(".%s.tmp", logFile)
var tempFile = filepath.Join(workingDir, fmt.Sprintf(".test-sync-start.tmp"))
Copy link
Member

Choose a reason for hiding this comment

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

Don;t need a Sprintf here is the log file name is not patterned from a variable. %s or whatevs.

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 what I meant to say is, why are you not using logFile

Copy link
Member

Choose a reason for hiding this comment

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

Also, logFile should already be a full absolute path, so I'm thinking that the filepath.Join is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This logic predates the PR. I think logFile is used by unison, and we're checking that tempFile is synced. I wonder what happens if a project ignores *.tmp files... might need a parameter or less fraught ext.

volumeName := cmd.GetVolumeName(ctx, cmd.Config)
// Determine the working directory for CWD-sensitive operations.
var workingDir string
if syncPath, err := cmd.DeriveLocalSyncPath(cmd.Config, ctx.String("sync-path")); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

could you not use workingDir here as it is already defined as a var and avoid the else all together?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I declare the var so it's in function instead of block scope, but the := operator is caught by the compiler as an overwrite. I could nest the whole function inside the if with reversed err check.

cmd.out.Verbose.Printf("Stopping sync with volume: %s", volumeName)
// Determine the working directory for CWD-sensitive operations.
var workingDir string
if syncPath, err := cmd.DeriveLocalSyncPath(cmd.Config, ctx.String("sync-path")); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Same, can we use workingDir here and remove the else

// Remove the Unison logfile.
func (cmd *ProjectSync) RemoveLogFile(logFile string, workingDir string) error {
command := exec.Command("rm", "-f", logFile)
command.Dir = workingDir
Copy link
Member

Choose a reason for hiding this comment

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

If logFile is a variable with workingDir already joined, you dont need to pass in workingDir and set it the command.Dir

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My reasoning here is that if it is worth having this broken out as a separate function, and an exported one at that, it makes sense to not make assumptions about the logFile path. In fact, you could argue that this should be checking the logFile path exists.

I don't have strong feelings about folding it back in or helping it to stand alone better. Do you have a preference?

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 keep it, I did not realize you intended to make that funciton for public consumption. If so, then don't call it RemoveLogFile just call it RemoveFile and rename the variable and then things make more sense as a general purpose function.

Copy link
Member

@febbraro febbraro left a comment

Choose a reason for hiding this comment

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

Left a few comments about paths and such

@grayside
Copy link
Contributor Author

  • Improved code flow around workingDir by nesting login inside if blocks.

@@ -38,14 +41,14 @@ func (cmd *ProjectSync) Commands() []cli.Command {
start := cli.Command{
Name: "sync:start",
Aliases: []string{"sync"},
Usage: "Start a unison sync on local project directory. Optionally provide a volume name.",
Usage: "Start a Unison sync on local project directory. Optionally provide a volume name.",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did some renaming so Unison is capitalized in messages.

var logFile = fmt.Sprintf("%s.log", volumeName)
exec.Command("rm", "-f", logFile).Run()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created util/filesystem.go for these basic kinds of operations.

// The use of os.Stat below is not subject to our working directory configuration,
// so to ensure we can stat the log file we convert it to an absolute path.
if logFilePath, err := util.AbsJoin(workingDir, logFile); err != nil {
cmd.out.Info.Print(err.Error())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've attempted to fix the inconsistency between absolute paths vs. working direct & relative paths throughout this file, but os.Stat is an edge case that needs support.

var timeoutLoopSleep = time.Duration(100) * time.Millisecond
// * 10 here because we loop once every 100 ms and we want to get to seconds
var timeoutLoops = timeoutSeconds * 10
var statSleep = time.Duration(syncWaitSeconds) * time.Second
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved statSleep up because we were previously looping over an otherwise unchanging operation.

cmd.out.Info.Print(err.Error())
} else {
// Create a temp file to cause a sync action
var tempFile = ".rig-check-sync-start"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed this further to remove .tmp suffix and introduce rig in the name to clarify what it is and avoid potential sync ignores around not syncing tempfiles.

@@ -0,0 +1,53 @@
package util
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All new code in this file.

@febbraro
Copy link
Member

So, it will choose either the sync-path provided, or the location of the config file, or the current working directory. Do we think that it makes sense to have a generally configurable path in the sync section of the config file? or do you think that is just generally captured under the --sync=path option and you'd encod ethat directly into your project scripts?

I will do a bit of a deeper review tonight, but i'm thinking this thing is just about good to merge, thanks for all the hard work on it.

@grayside
Copy link
Contributor Author

@febbraro my thinking is that projects will always sync relative to the outrigger.yml, because in that way if you connect via the CLI container all the pieces are the same, which I find tremendously valuable for general intuition. I could see the value of a sync-path option in outrigger.yml, but given the ignorenot modifier for Unison we theoretically support it already. https://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html#ignore

@grayside
Copy link
Contributor Author

Travis failed the branch on a partial refactoring I had in the util directory, to take the easy way out I added those changes to this PR.

I've broken up util.go into user_input.go and docker.go and folded image.go into docker.go. When there's more than one file I think semantically naming and categorizing everything makes it easier to find.

@febbraro febbraro merged commit 452e71a into develop Oct 25, 2017
@febbraro febbraro deleted the project-sync-from-root branch October 25, 2017 16:00
@grayside
Copy link
Contributor Author

This one wins the award for hardest easy PR I've landed in rig.

@grayside grayside added this to the v1.4 milestone Oct 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants