From d6b4ed9cc7f6b27c9180627e7d1fec4d698af28c Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 17 Jan 2019 19:46:28 +0300 Subject: Add support for --force option in bdep-release and bdep-publish --- bdep/ci.cxx | 12 +- bdep/project.cxx | 87 ++++++---- bdep/project.hxx | 6 +- bdep/publish.cli | 9 + bdep/publish.cxx | 59 ++++++- bdep/release.cli | 9 + bdep/release.cxx | 102 +++++++++--- tests/publish.testscript | 156 ++++++++++++++++-- tests/release.testscript | 418 +++++++++++++++++++++++++++++++---------------- 9 files changed, 637 insertions(+), 221 deletions(-) diff --git a/bdep/ci.cxx b/bdep/ci.cxx index 9298ef8..304818d 100644 --- a/bdep/ci.cxx +++ b/bdep/ci.cxx @@ -55,6 +55,10 @@ namespace bdep commit = move (s.commit); + // Note: not forcible. The use case could be to CI some commit from the + // past. But in this case we also won't have upstream. So maybe it will + // be better to invent the --commit option or some such. + // if (s.branch.empty ()) fail << "project directory is in the detached HEAD state" << info << "run 'git status' for details"; @@ -70,9 +74,15 @@ namespace bdep size_t p (path::traits::rfind_separator (s.upstream)); branch = p != string::npos ? string (s.upstream, p + 1) : s.upstream; + // Note: not forcible (for now). While the use case is valid, the + // current and committed package versions are likely to differ (in + // snapshot id). Obtaining the committed versions feels too hairy for + // now. + // if (s.staged || s.unstaged) fail << "project directory has uncommitted changes" << - info << "run 'git status' for details"; + info << "run 'git status' for details" << + info << "use 'git stash' to temporarily hide the changes"; // We definitely don't want to be ahead (upstream doesn't have this // commit) but there doesn't seem be anything wrong with being behind. diff --git a/bdep/project.cxx b/bdep/project.cxx index 8092867..cf79e41 100644 --- a/bdep/project.cxx +++ b/bdep/project.cxx @@ -13,6 +13,7 @@ #include using namespace std; +using namespace butl; namespace bdep { @@ -384,50 +385,66 @@ namespace bdep } } - standard_version - package_version (const common_options& o, - const dir_path& cfg, - const package_name& p) + // Obtain build2 project info for package source or output directories. + // + static b_project_info + package_info (const common_options& o, const dir_path& d) { - using namespace butl; - - // We could have used bpkg-pkg-status but then we would have to deal with - // iterations. So we use the build system's info meta-operation directly. - // try { - // Note: the package directory inside the configuration is a bit of an - // assumption. - // - b_project_info pi ( - b_info ((dir_path (cfg) /= p.string ()), - verb, - [] (const char* const args[], size_t n) - { - if (verb >= 3) - print_process (args, n); - }, - path (name_b (o)), - exec_dir, - o.build_option ())); - - if (pi.version.empty ()) - fail << "empty version for package " << p; - - // Verify the name for good measure. - // - if (pi.project != p) - fail << "name mismatch for package " << p; - - return move (pi.version); + return b_info (d, + verb, + [] (const char* const args[], size_t n) + { + if (verb >= 3) + print_process (args, n); + }, + path (name_b (o)), + exec_dir, + o.build_option ()); } catch (const b_error& e) { if (e.normal ()) throw failed (); // Assume the build2 process issued diagnostics. - fail << "unable to obtain package " << p << " project info: " << e - << endf; + fail << "unable to obtain project info for package directory " << d + << ": " << e << endf; } } + + standard_version + package_version (const common_options& o, const dir_path& d) + { + b_project_info pi (package_info (o, d)); + + if (pi.version.empty ()) + fail << "empty version for package directory " << d; + + return move (pi.version); + } + + standard_version + package_version (const common_options& o, + const dir_path& cfg, + const package_name& p) + { + // We could have used bpkg-pkg-status but then we would have to deal with + // iterations. So we use the build system's info meta-operation directly. + // + // Note: the package directory inside the configuration is a bit of an + // assumption. + // + b_project_info pi (package_info (o, (dir_path (cfg) /= p.string ()))); + + if (pi.version.empty ()) + fail << "empty version for package " << p; + + // Verify the name for good measure. + // + if (pi.project != p) + fail << "name mismatch for package " << p; + + return move (pi.version); + } } diff --git a/bdep/project.hxx b/bdep/project.hxx index 47373b0..de39ee5 100644 --- a/bdep/project.hxx +++ b/bdep/project.hxx @@ -236,9 +236,13 @@ namespace bdep void verify_project_packages (const project_packages&, const configurations&); - // Determine the version of a package in the specified configuration. + // Determine the version of a package in the specified package (first + // version) or configuration (second version) directory. // standard_version + package_version (const common_options&, const dir_path& pkg); + + standard_version package_version (const common_options&, const dir_path& cfg, const package_name&); diff --git a/bdep/publish.cli b/bdep/publish.cli index 1c05cd0..cba7436 100644 --- a/bdep/publish.cli +++ b/bdep/publish.cli @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +include ; + include ; "\section=1" @@ -125,6 +127,13 @@ namespace bdep for details." } + std::set --force + { + "", + "Force publishing, disabling the specified check. Repeat this option to + disable multiple checks." + } + string --simulate { "", diff --git a/bdep/publish.cxx b/bdep/publish.cxx index 3c64706..c97c652 100644 --- a/bdep/publish.cxx +++ b/bdep/publish.cxx @@ -120,6 +120,27 @@ namespace bdep }; vector pkgs; + // If the project is under recognized VCS, it feels right to fail if it is + // uncommitted to decrease chances of publishing package modifications + // under the same version. It still make sense to allow submitting + // uncommitted packages if requested explicitly, for whatever reason. + // + bool git_repo (git_repository (prj)); + optional uncommitted; // Absent if unrecognized VCS. + + if (git_repo) + { + git_repository_status st (git_status (prj)); + uncommitted = st.unstaged || st.staged; + + if (*uncommitted && + o.force ().find ("uncommitted") == o.force ().end ()) + fail << "project directory has uncommitted changes" << + info << "run 'git status' for details" << + info << "use 'git stash' to temporarily hide the changes" << + info << "use --force=uncommitted to publish anyway"; + } + for (package_location& pl: pkg_locs) { package_name n (move (pl.name)); @@ -131,8 +152,31 @@ namespace bdep // For example, is it correct to consider a "between betas" snapshot a // beta version? // - if (v.snapshot ()) - fail << "package " << n << " version " << v << " is a snapshot"; + // Seems nothing wrong with submitting snapshots generally. We do this + // for staging most of the time. The below logic of choosing a default + // section requires no changes. Also, the submission service can + // probably have some policy of version acceptance, rejecting some + // version types for some sections. For example, rejecting pre-release + // for stable section and snapshots for any section. + // + // Note, however, that if the project is under an unrecognized VCS or + // the snapshot is uncommitted, then we make it non-forcible. Failed + // that, we might end up publishing distinct snapshots under the same + // temporary version. (To be more precise, we should have checked for + // the latest snapshot but that would require getting the original + // version from the package manifest, which feels too hairy for now). + // + bool non_forcible; + if (v.snapshot () && + ((non_forcible = (!uncommitted || *uncommitted)) || + o.force ().find ("snapshot") == o.force ().end ())) + { + diag_record dr (fail); + dr << "package " << n << " version " << v << " is a snapshot"; + + if (!non_forcible) + dr << info << "use --force=snapshot to publish anyway"; + } // Per semver we treat zero major versions as alpha. // @@ -201,11 +245,18 @@ namespace bdep // directly to prepare the distribution. If/when we have bpkg-pkg-dist, // we may want to switch to that. // + // We need to specify config.dist.uncommitted=true for a snapshot since + // build2's version module by default does not allow distribution of + // uncommitted projects. + // run_b (o, "dist:", (dir_path (cfg) /= p.name.string ()).representation (), "config.dist.root=" + dr.representation (), "config.dist.archives=tar.gz", - "config.dist.checksums=sha256"); + "config.dist.checksums=sha256", + (uncommitted && *uncommitted + ? "config.dist.uncommitted=true" + : nullptr)); // This is the canonical package archive name that we expect dist to // produce. @@ -297,7 +348,7 @@ namespace bdep // // See if this is a VCS repository we recognize. // - if (ctrl && git_repository (prj)) + if (ctrl && git_repo) { // Checkout the build2-control branch into a separate working tree not // to interfere with the user's stuff. diff --git a/bdep/release.cli b/bdep/release.cli index 651288c..c90ec29 100644 --- a/bdep/release.cli +++ b/bdep/release.cli @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +include ; + include ; "\section=1" @@ -151,6 +153,13 @@ namespace bdep "Open the development cycle with the next major version." } + std::set --force + { + "", + "Force releasing, disabling the specified check. Repeat this option to + disable multiple checks." + } + bool --yes|-y { "Don't prompt for confirmation before releasing." diff --git a/bdep/release.cxx b/bdep/release.cxx index dfa092c..cb4cac9 100644 --- a/bdep/release.cxx +++ b/bdep/release.cxx @@ -66,17 +66,19 @@ namespace bdep static void plan_tag (const cmd_release_options& o, project& prj, const status& st) { - // While there is nothing wrong with having uncommitted changes while - // tagging, in our case we may end up with a wrong tag if the version in - // the (modified) manifest does not correspond to the latest commit. + // Note: not forcible. While there is nothing wrong with having + // uncommitted changes while tagging, in our case we may end up with a + // wrong tag if the version in the (modified) manifest does not correspond + // to the latest commit. // // Note that if we are called as part of releasing a new revision, then - // the project will have some changes staged (see plan_revision() for + // the project may have some changes staged (see plan_revision() for // details). // if ((st.staged && !o.revision ()) || st.unstaged) fail << "project directory has uncommitted changes" << - info << "run 'git status' for details"; + info << "run 'git status' for details" << + info << "use 'git stash' to temporarily hide the changes"; // All the versions are the same sans the revision. Note that our version // can be either current (--tag mode) or release (part of the release). @@ -86,12 +88,26 @@ namespace bdep ? *pkg.release_version : pkg.current_version); - if (cv.snapshot ()) - fail << "current version " << cv << " is a snapshot"; + // It could be desirable to tag a snapshot for some kind of a pseudo- + // release. For example, tag a snapshot for QA or some such. Note: can't + // happen as part of a release or revision. + // + if (cv.snapshot () && o.force ().find ("snapshot") == o.force ().end ()) + fail << "current version " << cv << " is a snapshot"<< + info << "use --force=snapshot to tag anyway"; // Canonical version tag without epoch or revision. // - prj.tag = "v" + cv.string_project (); + // For tagging a snapshot we need to use the actual package version + // (replacing .z with the actual snapshot information). Note: for + // snapshots we are always tagging the current version (see above). + // + const standard_version& v ( + cv.latest_snapshot () + ? package_version (o, pkg.manifest.directory ()) + : cv); + + prj.tag = "v" + v.string_project (); // Replace the existing tag only if this is a revision. // @@ -104,9 +120,14 @@ namespace bdep // There could be changes already added to the index but otherwise the // repository should be clean. // - if (st.unstaged) + // Note: not forcible. Generally, we don't touch unstanged changes (which + // include untracked files). + // + if (st.unstaged && !o.no_commit ()) fail << "project directory has unstaged changes" << - info << "run 'git status' for details"; + info << "run 'git status' for details" << + info << "use 'git add' to add the changes to this commit" << + info << "use 'git stash' to temporarily hide the changes"; // All the versions are the same sans the revision. Note that our version // can be either current (--open mode) or release (part of the release). @@ -151,10 +172,16 @@ namespace bdep "" /* snapshot_id */); }; + // Note: not forcible. The following (till the end of the function) checks + // prevent skipping a release. Hard to think of a use case but we probably + // could allow this as it doesn't break anything (released versions need + // not be contiguous). Let's, however, postpone this until a real use + // case comes up in order not to complicate things needlessly. + // if (cv.snapshot ()) { - // The cv variable refers the current version as the release version - // cannot be a snapshot. + // The cv variable refers to the current version since the release + // version cannot be a snapshot. // assert (!pkg.release_version); @@ -209,9 +236,14 @@ namespace bdep // There could be changes already added to the index but otherwise the // repository should be clean. // - if (st.unstaged) + // Note: not forcible. Generally, we don't touch unstanged changes (which + // include untracked files). + // + if (st.unstaged && !o.no_commit ()) fail << "project directory has unstaged changes" << - info << "run 'git status' for details"; + info << "run 'git status' for details" << + info << "use 'git add' to add the changes to this commit" << + info << "use 'git stash' to temporarily hide the changes"; // All the current versions are the same sans the revision. // @@ -219,6 +251,9 @@ namespace bdep // Change the release version to the next (pre-)release. // + // Note: not forcible. The following (till the end of the function) checks + // prevent skipping a release. The same reasoning as in plan_open(). + // standard_version rv; if (cv.snapshot ()) { @@ -319,19 +354,35 @@ namespace bdep // There must be changes already added to the index but otherwise the // repository should be clean. // - if (st.unstaged) + // Note: not forcible. Generally, we don't touch unstanged changes (which + // include untracked files). + // + if (st.unstaged && !o.no_commit ()) fail << "project directory has unstaged changes" << - info << "run 'git status' for details"; + info << "run 'git status' for details" << + info << "use 'git add' to add the changes to this commit" << + info << "use 'git stash' to temporarily hide the changes"; - if (!st.staged) + // Note: the use-case for forcing would be to create a commit to be + // squashed later. + // + // Also, don't complain if we are not committing. + // + if (!st.staged && + !o.no_commit () && + o.force ().find ("unchanged") == o.force ().end ()) fail << "project directory has no staged changes" << info << "revision increment must be committed together with " - << "associated changes"; + << "associated changes" << + info << "use --force=unchanged to release anyway"; // All the current versions are the same sans the revision. // const standard_version& cv (prj.packages.front ().current_version); + // Note: not forcible. Doesn't seem to make sense to release a revision + // for snapshot (just release another snapshot). + // if (cv.snapshot ()) fail << "current version " << cv << " is a snapshot"; @@ -460,8 +511,12 @@ namespace bdep return true; })); + // // Validate the *-file manifest values expansion. // + // Note: not forcible. Allowing releasing broken packages we are just + // asking for trouble and long mailing list threads. + // m.load_files ([&f] (const string& n, const path& p) { path vf (f.directory () / p); @@ -696,8 +751,7 @@ namespace bdep catch (const io_error& e) { fail << "unable to read/write " << p.manifest << ": " << e << - info << "run 'git -C " << prj.path << " checkout -- ./' to revert " - << "any changes and try again"; + info << "use 'git checkout' to revert any changes and try again"; } } @@ -782,14 +836,12 @@ namespace bdep { // If we are releasing, then the release/revision version have // already been written to the manifests and the changes have been - // committed. Thus, the user should re-try with the --open option + // committed. Thus, the user should re-try with the --open option // in this case. // fail << "unable to read/write " << p.manifest << ": " << e << - info << "run 'git -C " << prj.path << " checkout -- ./' to revert " - << "any changes and try again" << (pkg.release_version - ? " with --open" - : ""); + info << "use 'git checkout' to revert any changes and try again" + << (pkg.release_version ? " with --open" : ""); } } diff --git a/tests/publish.testscript b/tests/publish.testscript index 7913272..df3a817 100644 --- a/tests/publish.testscript +++ b/tests/publish.testscript @@ -37,21 +37,25 @@ init += $cxx -d prj 2>! &prj/**/bootstrap/*** windows = ($cxx.target.class == 'windows') +g = git -C prj >! 2>! + # Note that using the same package name and version for tests may result in # duplicate submissions. We will use unique version for each test, # incrementing the patch version for 1.0.X. # -# Next version to use: 1.0.15 +# Next version to use: 1.0.19 # : submit : { test.arguments += --control 'none' + +$clone_prj + : single-pkg : { - test.arguments += --simulate 'success' + test.arguments += --force=uncommitted --simulate 'success' : single-cfg : @@ -65,7 +69,7 @@ windows = ($cxx.target.class == 'windows') upgrade prj/1.0.1 submitting prj-1.0.1.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.1)?% + %package submission is queued(: \.*prj/1.0.1)?%d %reference: .{12}% EOE } @@ -90,12 +94,30 @@ windows = ($cxx.target.class == 'windows') $* --all 2>'error: multiple configurations specified for publish' != 0 } + + : snapshot + : + { + $clone_root_prj; + $init -C @cfg &prj-cfg/***; + sed -i -e 's/^(version:) .*$/\1 1.0.15-a.0.z/' prj/manifest; + + $* 2>>EOE != 0; + synchronizing: + upgrade prj/1.0.15-a.0.19700101000000 + error: package prj version 1.0.15-a.0.19700101000000 is a snapshot + EOE + + $* --force=snapshot 2>>~%EOE% != 0 + error: package prj version 1.0.15-a.0.19700101000000 is a snapshot + EOE + } } : multi-pkg : { - test.arguments += --simulate 'success' + test.arguments += --force=uncommitted --simulate 'success' +$new -t empty prj &prj/*** +$new --package -t lib libprj -d prj @@ -112,11 +134,11 @@ windows = ($cxx.target.class == 'windows') $* 2>>~%EOE% submitting libprj-1.0.2.tar.gz %.* - %package submission is queued(: .*libprj/1\.0\.2)?% + %package submission is queued(: \.*libprj/1.0.2)?%d %reference: .{12}% submitting prj-1.0.2.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.2)?% + %package submission is queued(: \.*prj/1.0.2)?%d %reference: .{12}% EOE } @@ -135,7 +157,7 @@ windows = ($cxx.target.class == 'windows') $* 2>>~%EOE% submitting libprj-1.0.3.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.3)?% + %package submission is queued(: \.*prj/1.0.3)?%d %reference: .{12}% EOE } @@ -169,19 +191,118 @@ windows = ($cxx.target.class == 'windows') %warning: publishing using staged build2 toolchain%? continue? [y/n] submitting libprj-1.0.4.tar.gz %.* - %package submission is queued\(: .*libprj/1\\.0\\.4\)?% + %package submission is queued\(: \\.*libprj/1.0.4\)?%d %reference: .{12}% submitting prj-1.0.4.tar.gz %.* - %package submission is queued\(: .*prj/1\\.0\\.4\)?% + %package submission is queued\(: \\.*prj/1.0.4\)?%d %reference: .{12}% EOE } } + : commited-prj + : + { + test.arguments += --simulate 'success' + + clone_prj = cp --no-cleanup -p -r ../prj ./ &prj/*** + + +$clone_prj + + +$g config user.name 'Test Script' + +$g config user.email 'testscript@example.com' + +$g add '*' + +$g commit -m 'Create' + + : final + : + { + $clone_prj; + $init -C @cfg &prj-cfg/***; + sed -i -e 's/^(version:) .*$/\1 1.0.16/' prj/manifest; + + $* 2>>~%EOE% != 0; + synchronizing: + upgrade prj/1.0.16 + error: project directory has uncommitted changes + info: run 'git status' for details + info: use 'git stash' to temporarily hide the changes + info: use --force=uncommitted to publish anyway + EOE + + $g commit -a -m 'Version'; + + $* 2>>~%EOE% + submitting prj-1.0.16.tar.gz + %.* + %package submission is queued(: \.*prj/1.0.16)?%d + %reference: .{12}% + EOE + } + + : snapshot + : + { + $clone_prj; + $init -C @cfg &prj-cfg/***; + sed -i -e 's/^(version:) .*$/\1 1.0.17-a.0.z/' prj/manifest; + + $* 2>>~%EOE% != 0; + synchronizing: + % upgrade prj/1.0.17-a.0.\.+%d + error: project directory has uncommitted changes + info: run 'git status' for details + info: use 'git stash' to temporarily hide the changes + info: use --force=uncommitted to publish anyway + EOE + + $g commit -a -m 'Version'; + + $* 2>>~%EOE% != 0; + synchronizing: + % upgrade prj/1.0.17-a.0.\.+%d + %error: package prj version 1.0.17-a.0.\.+ is a snapshot%d + info: use --force=snapshot to publish anyway + EOE + + $* --force=snapshot 2>>~%EOE% + %submitting prj-1.0.17-a.0.\.+.tar.gz%d + %.* + %package submission is queued(: \.*prj/1.0.17-a.0.\.+)?%d + %reference: .{12}% + EOE + } + } + + : non-vsc-prj + : + { + test.arguments += --simulate 'success' + + clone_prj = cp --no-cleanup -p -r ../prj ./ &prj/***; + + $clone_prj; + rm -r -f prj/.git; + + $init -C @cfg &prj-cfg/***; + sed -i -e 's/^(version:) .*$/\1 1.0.18/' prj/manifest; + + $* 2>>~%EOE% + synchronizing: + upgrade prj/1.0.18 + submitting prj-1.0.18.tar.gz + %.* + %package submission is queued(: \.*prj/1.0.18)?%d + %reference: .{12}% + EOE + } + : failure : { + test.arguments += --force=uncommitted + : duplicate-archive : { @@ -248,7 +369,8 @@ windows = ($cxx.target.class == 'windows') # simulation. We specify it to enable the control branch-related # functionality. # - test.arguments += --simulate 'success' --control 'http://example.com/rep.git' + test.arguments += --force=uncommitted --simulate 'success' \ +--control 'http://example.com/rep.git' # Create the remote repository. # @@ -257,8 +379,6 @@ windows = ($cxx.target.class == 'windows') +$clone_prj - g = git -C prj >! 2>! - +$g config user.name 'Test Script' +$g config user.email 'testscript@example.com' @@ -291,7 +411,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.8.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.8)?% + %package submission is queued(: \.*prj/1.0.8)?%d %reference: .{12}% EOE @@ -307,7 +427,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.9.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.9)?% + %package submission is queued(: \.*prj/1.0.9)?%d %reference: .{12}% EOE @@ -325,7 +445,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.10.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.10)?% + %package submission is queued(: \.*prj/1.0.10)?%d %reference: .{12}% EOE @@ -407,7 +527,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.12.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.12)?% + %package submission is queued(: \.*prj/1.0.12)?%d %reference: .{12}% EOE @@ -425,7 +545,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.13.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.13)?% + %package submission is queued(: \.*prj/1.0.13)?%d %reference: .{12}% EOE @@ -466,7 +586,7 @@ windows = ($cxx.target.class == 'windows') %.* submitting prj-1.0.14.tar.gz %.* - %package submission is queued(: .*prj/1\.0\.14)?% + %package submission is queued(: \.*prj/1.0.14)?%d %reference: .{12}% EOE diff --git a/tests/release.testscript b/tests/release.testscript index 98fddad..45d6867 100644 --- a/tests/release.testscript +++ b/tests/release.testscript @@ -61,94 +61,100 @@ log2 = $gp2 log '--pretty=format:"%d %s"' : release : { - test.arguments += --push - - : patch + : version : { - $clone_root_repos; + test.arguments += --push - $*; + : patch + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO%; - % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d - (tag: v0.1.0) Release version 0.1.0 - Create - EOO + $*; - cat prj2/manifest >>~%EOO% - %.* - name: prj - version: 0.2.0-a.0.z - summary: prj executable - %.* + $clone2; + $log2 >>:~%EOO%; + % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d + (tag: v0.1.0) Release version 0.1.0 + Create EOO - } - : alpha - : - { - $clone_root_repos; + cat prj2/manifest >>~%EOO% + %.* + name: prj + version: 0.2.0-a.0.z + summary: prj executable + %.* + EOO + } - $* --alpha; + : alpha + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 0.1.0-a.1.z%d - (tag: v0.1.0-a.1) Release version 0.1.0-a.1 - Create - EOO - } + $* --alpha; - : beta - : - { - $clone_root_repos; + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 0.1.0-a.1.z%d + (tag: v0.1.0-a.1) Release version 0.1.0-a.1 + Create + EOO + } - $* --beta; + : beta + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d - (tag: v0.1.0-b.1) Release version 0.1.0-b.1 - Create - EOO - } + $* --beta; - : minor - : - { - $clone_root_repos; + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d + (tag: v0.1.0-b.1) Release version 0.1.0-b.1 + Create + EOO + } - $* --minor; + : minor + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 0.3.0-a.0.z%d - (tag: v0.2.0) Release version 0.2.0 - Create - EOO - } + $* --minor; - : major - : - { - $clone_root_repos; + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 0.3.0-a.0.z%d + (tag: v0.2.0) Release version 0.2.0 + Create + EOO + } - $* --major; + : major + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 1.1.0-a.0.z%d - (tag: v1.0.0) Release version 1.0.0 - Create - EOO + $* --major; + + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 1.1.0-a.0.z%d + (tag: v1.0.0) Release version 1.0.0 + Create + EOO + } } : open : { + test.arguments += --push + : beta : { @@ -243,87 +249,107 @@ log2 = $gp2 log '--pretty=format:"%d %s"' } } - : no-open + : no : { - $clone_root_repos; + test.arguments += --push - $* --no-open; + : open + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO%; - % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d - Create - EOO + $* --no-open; - $* --open; + $clone2; + $log2 >>:~%EOO%; + % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d + Create + EOO - $pull2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d - (tag: v0.1.0) Release version 0.1.0 - Create - EOO - } + $* --open; - : no-tag - : - { - $clone_root_repos; + $pull2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d + (tag: v0.1.0) Release version 0.1.0 + Create + EOO + } - $* --no-tag; + : tag + : + { + $clone_root_repos; - $clone2; - $log2 >>:~%EOO%; - % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d - Release version 0.1.0 - Create - EOO + $* --no-tag; - $* --tag 2>'error: current version 0.2.0-a.0.z is a snapshot' != 0 - } + $clone2; + $log2 >>:~%EOO%; + % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d + Release version 0.1.0 + Create + EOO - : no-tag-no-open - : - { - $clone_root_repos; + $* --tag 2>>EOE != 0; + error: current version 0.2.0-a.0.z is a snapshot + info: use --force=snapshot to tag anyway + EOE - $* --no-tag --no-open; + $* --tag --force=snapshot; - $clone2; - $log2 >>:~%EOO%; - % \(HEAD -> master, \.*\) Release version 0.1.0%d - Create - EOO + $pull2; + $log2 >>:~%EOO% + % \(HEAD -> master, tag: v0.2.0-a.0.\.*.\.*\) Change version to 0.2.0-a.0.z%d + Release version 0.1.0 + Create + EOO + } - $* --tag; + : tag-open + : + { + $clone_root_repos; - $pull2; - $log2 >>:~%EOO%; - % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d - Create - EOO + $* --no-tag --no-open; - $* --open; + $clone2; + $log2 >>:~%EOO%; + % \(HEAD -> master, \.*\) Release version 0.1.0%d + Create + EOO - $pull2; - $log2 >>:~%EOO% - % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d - (tag: v0.1.0) Release version 0.1.0 - Create - EOO - } + $* --tag; - : no-commit - : - { - $clone_root_repos; + $pull2; + $log2 >>:~%EOO%; + % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d + Create + EOO + + $* --open; + + $pull2; + $log2 >>:~%EOO% + % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d + (tag: v0.1.0) Release version 0.1.0 + Create + EOO + } + + : commit + : + { + $clone_root_repos; - $* --no-commit 2>'error: both --push and --no-commit specified' != 0 + $* --no-commit 2>'error: both --push and --no-commit specified' != 0 + } } : validate-manifest { + test.arguments += --push + : file-value : { @@ -363,6 +389,31 @@ log2 = $gp2 log '--pretty=format:"%d %s"' EOE } } + + : unstaged + : + { + $clone_root_repos; + echo '' >+ prj/manifest; + + $* 2>>EOE != 0; + error: project directory has unstaged changes + info: run 'git status' for details + info: use 'git add' to add the changes to this commit + info: use 'git stash' to temporarily hide the changes + EOE + + $* --no-commit; + + $gp commit -a -m 'Release version'; + $* --tag --push; + + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, tag: v0.1.0, \.*\) Release version%d + Create + EOO + } } : revision @@ -371,17 +422,74 @@ log2 = $gp2 log '--pretty=format:"%d %s"' +$clone_root_repos +$* --no-open --push - +echo '' >+ prj/manifest - +$gp add manifest + test.arguments += --revision - test.arguments += --revision --push + : changes-staged + : + { + test.arguments += --push - : default + +$clone_repos + + +echo '' >+ prj/manifest + +$gp add manifest + + : default + : + { + $clone_repos; + + $* 2>>~%EOE%; + %Updated tag 'v0.1.0' \(was \.*\)%d + EOE + + $clone2; + $log2 >>:~%EOO% + % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d + Release version 0.1.0 + Create + EOO + } + + : no-tag + : + { + $clone_repos; + + $* --no-tag; + + $clone2; + $log2 >>:~%EOO%; + % \(HEAD -> master, \.*\) Release version 0.1.0\+1%d + (tag: v0.1.0) Release version 0.1.0 + Create + EOO + + $release --tag --push --yes -d prj; + + $pull2 --tags; # Updates the existing local tag. + $log2 >>:~%EOO% + % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d + Release version 0.1.0 + Create + EOO + } + } + + : no-changes-staged : { + test.arguments += --push + $clone_repos; - $* 2>>~%EOE%; + $* 2>>~%EOE% != 0; + error: project directory has no staged changes + info: revision increment must be committed together with associated changes + info: use --force=unchanged to release anyway + EOE + + $* --force=unchanged 2>>~%EOE%; %Updated tag 'v0.1.0' \(was \.*\)%d EOE @@ -393,26 +501,62 @@ log2 = $gp2 log '--pretty=format:"%d %s"' EOO } - : no-tag + : unstaged : { $clone_repos; - $* --no-tag; + echo '' >+ prj/manifest; + + $* 2>>EOE != 0; + error: project directory has unstaged changes + info: run 'git status' for details + info: use 'git add' to add the changes to this commit + info: use 'git stash' to temporarily hide the changes + EOE + + $* --no-commit; + + $gp commit -a -m 'Release revision'; + $release --tag --push --yes -d prj; $clone2; - $log2 >>:~%EOO%; - % \(HEAD -> master, \.*\) Release version 0.1.0\+1%d - (tag: v0.1.0) Release version 0.1.0 + $log2 >>:~%EOO% + % \(HEAD -> master, tag: v0.1.0, \.*\) Release revision%d + Release version 0.1.0 Create EOO + } + } - $release --tag --push --yes -d prj; + : open + : + { + : unstaged + : + { + $clone_root_repos; + + $* --no-open; + + echo '' >+ prj/manifest; - $pull2 --tags; # Updates the existing local tag. + $* --open 2>>EOE != 0; + error: project directory has unstaged changes + info: run 'git status' for details + info: use 'git add' to add the changes to this commit + info: use 'git stash' to temporarily hide the changes + EOE + + $* --open --no-commit; + + $gp commit -a -m 'Open dev cycle'; + $gp push origin HEAD --tags; + + $clone2; $log2 >>:~%EOO% - % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d - Release version 0.1.0 + % \(HEAD -> master, \.*\) Open dev cycle%d + (tag: v0.1.0) Release version 0.1.0 Create EOO } -- cgit v1.1