252 |
252 |
253 [clone-bundles] |
253 [clone-bundles] |
254 auto-generate.on-change=yes |
254 auto-generate.on-change=yes |
255 auto-generate.formats= zstd-v2, gzip-v2 |
255 auto-generate.formats= zstd-v2, gzip-v2 |
256 |
256 |
|
257 Automatic Inline serving |
|
258 ........................ |
|
259 |
|
260 The simplest way to serve the generated bundle is through the Mercurial |
|
261 protocol. However it is not the most efficient as request will still be served |
|
262 by that main server. It is useful in case where authentication is complexe or |
|
263 when an efficient mirror system is already in use anyway. See the `inline |
|
264 clonebundles` section above for details about inline clonebundles |
|
265 |
|
266 To automatically serve generated bundle through inline clonebundle, simply set |
|
267 the following option:: |
|
268 |
|
269 auto-generate.serve-inline=yes |
|
270 |
|
271 Enabling this option disable the managed upload and serving explained below. |
|
272 |
257 Bundles Upload and Serving: |
273 Bundles Upload and Serving: |
258 ........................... |
274 ........................... |
|
275 |
|
276 This is the most efficient way to serve automatically generated clone bundles, |
|
277 but requires some setup. |
259 |
278 |
260 The generated bundles need to be made available to users through a "public" URL. |
279 The generated bundles need to be made available to users through a "public" URL. |
261 This should be donne through `clone-bundles.upload-command` configuration. The |
280 This should be donne through `clone-bundles.upload-command` configuration. The |
262 value of this command should be a shell command. It will have access to the |
281 value of this command should be a shell command. It will have access to the |
263 bundle file path through the `$HGCB_BUNDLE_PATH` variable. And the expected |
282 bundle file path through the `$HGCB_BUNDLE_PATH` variable. And the expected |
342 cmdtable = {} |
361 cmdtable = {} |
343 command = registrar.command(cmdtable) |
362 command = registrar.command(cmdtable) |
344 |
363 |
345 configitem(b'clone-bundles', b'auto-generate.on-change', default=False) |
364 configitem(b'clone-bundles', b'auto-generate.on-change', default=False) |
346 configitem(b'clone-bundles', b'auto-generate.formats', default=list) |
365 configitem(b'clone-bundles', b'auto-generate.formats', default=list) |
|
366 configitem(b'clone-bundles', b'auto-generate.serve-inline', default=False) |
347 configitem(b'clone-bundles', b'trigger.below-bundled-ratio', default=0.95) |
367 configitem(b'clone-bundles', b'trigger.below-bundled-ratio', default=0.95) |
348 configitem(b'clone-bundles', b'trigger.revs', default=1000) |
368 configitem(b'clone-bundles', b'trigger.revs', default=1000) |
349 |
369 |
350 configitem(b'clone-bundles', b'upload-command', default=None) |
370 configitem(b'clone-bundles', b'upload-command', default=None) |
351 |
371 |
751 def upload_bundle(repo, bundle): |
771 def upload_bundle(repo, bundle): |
752 """upload the result of a GeneratingBundle and return a GeneratedBundle |
772 """upload the result of a GeneratingBundle and return a GeneratedBundle |
753 |
773 |
754 The upload is done using the `clone-bundles.upload-command` |
774 The upload is done using the `clone-bundles.upload-command` |
755 """ |
775 """ |
756 cmd = repo.ui.config(b'clone-bundles', b'upload-command') |
776 inline = repo.ui.config(b'clone-bundles', b'auto-generate.serve-inline') |
757 url = repo.ui.config(b'clone-bundles', b'url-template') |
|
758 basename = repo.vfs.basename(bundle.filepath) |
777 basename = repo.vfs.basename(bundle.filepath) |
759 filepath = procutil.shellquote(bundle.filepath) |
778 if inline: |
760 variables = { |
779 dest_dir = repo.vfs.join(bundlecaches.BUNDLE_CACHE_DIR) |
761 b'HGCB_BUNDLE_PATH': filepath, |
780 repo.vfs.makedirs(dest_dir) |
762 b'HGCB_BUNDLE_BASENAME': basename, |
781 dest = repo.vfs.join(dest_dir, basename) |
763 } |
782 util.copyfiles(bundle.filepath, dest, hardlink=True) |
764 env = procutil.shellenviron(environ=variables) |
783 url = bundlecaches.CLONEBUNDLESCHEME + basename |
765 ret = repo.ui.system(cmd, environ=env) |
784 return bundle.uploaded(url, basename) |
766 if ret: |
785 else: |
767 raise error.Abort(b"command returned status %d: %s" % (ret, cmd)) |
786 cmd = repo.ui.config(b'clone-bundles', b'upload-command') |
768 url = ( |
787 url = repo.ui.config(b'clone-bundles', b'url-template') |
769 url.decode('utf8') |
788 filepath = procutil.shellquote(bundle.filepath) |
770 .format(basename=basename.decode('utf8')) |
789 variables = { |
771 .encode('utf8') |
790 b'HGCB_BUNDLE_PATH': filepath, |
772 ) |
791 b'HGCB_BUNDLE_BASENAME': basename, |
773 return bundle.uploaded(url, basename) |
792 } |
|
793 env = procutil.shellenviron(environ=variables) |
|
794 ret = repo.ui.system(cmd, environ=env) |
|
795 if ret: |
|
796 raise error.Abort(b"command returned status %d: %s" % (ret, cmd)) |
|
797 url = ( |
|
798 url.decode('utf8') |
|
799 .format(basename=basename.decode('utf8')) |
|
800 .encode('utf8') |
|
801 ) |
|
802 return bundle.uploaded(url, basename) |
774 |
803 |
775 |
804 |
776 def delete_bundle(repo, bundle): |
805 def delete_bundle(repo, bundle): |
777 """delete a bundle from storage""" |
806 """delete a bundle from storage""" |
778 assert bundle.ready |
807 assert bundle.ready |
779 msg = b'clone-bundles: deleting bundle %s\n' |
808 |
|
809 inline = bundle.file_url.startswith(bundlecaches.CLONEBUNDLESCHEME) |
|
810 |
|
811 if inline: |
|
812 msg = b'clone-bundles: deleting inline bundle %s\n' |
|
813 else: |
|
814 msg = b'clone-bundles: deleting bundle %s\n' |
780 msg %= bundle.basename |
815 msg %= bundle.basename |
781 if repo.ui.configbool(b'devel', b'debug.clonebundles'): |
816 if repo.ui.configbool(b'devel', b'debug.clonebundles'): |
782 repo.ui.write(msg) |
817 repo.ui.write(msg) |
783 else: |
818 else: |
784 repo.ui.debug(msg) |
819 repo.ui.debug(msg) |
785 |
820 |
786 cmd = repo.ui.config(b'clone-bundles', b'delete-command') |
821 if inline: |
787 variables = { |
822 inline_path = repo.vfs.join( |
788 b'HGCB_BUNDLE_URL': bundle.file_url, |
823 bundlecaches.BUNDLE_CACHE_DIR, |
789 b'HGCB_BASENAME': bundle.basename, |
824 bundle.basename, |
790 } |
825 ) |
791 env = procutil.shellenviron(environ=variables) |
826 util.tryunlink(inline_path) |
792 ret = repo.ui.system(cmd, environ=env) |
827 else: |
793 if ret: |
828 cmd = repo.ui.config(b'clone-bundles', b'delete-command') |
794 raise error.Abort(b"command returned status %d: %s" % (ret, cmd)) |
829 variables = { |
|
830 b'HGCB_BUNDLE_URL': bundle.file_url, |
|
831 b'HGCB_BASENAME': bundle.basename, |
|
832 } |
|
833 env = procutil.shellenviron(environ=variables) |
|
834 ret = repo.ui.system(cmd, environ=env) |
|
835 if ret: |
|
836 raise error.Abort(b"command returned status %d: %s" % (ret, cmd)) |
795 |
837 |
796 |
838 |
797 def auto_bundle_needed_actions(repo, bundles, op_id): |
839 def auto_bundle_needed_actions(repo, bundles, op_id): |
798 """find the list of bundles that need action |
840 """find the list of bundles that need action |
799 |
841 |