@@ -42,16 +42,53 @@ class BaseSphinx(BaseBuilder):
42
42
# an output file, the parsed source files are cached as "doctree pickles".
43
43
sphinx_doctrees_dir = "_build/doctrees"
44
44
45
- # Output directory relative to where the repository was cloned
46
- # (e.g. "_readthedocs/<format> ")
45
+ # Output directory relative to $READTHEDOCS_OUTPUT
46
+ # (e.g. "html", "htmlzip" or "pdf ")
47
47
relative_output_dir = None
48
48
49
49
def __init__ (self , * args , ** kwargs ):
50
50
super ().__init__ (* args , ** kwargs )
51
51
self .config_file = self .config .sphinx .configuration
52
- self .absolute_output_dir = os .path .abspath (
53
- os .path .join (self .project_path , self .relative_output_dir )
52
+
53
+ # We cannot use `$READTHEDOCS_OUTPUT` environment variable for
54
+ # `absolute_host_output_dir` because it's not defined in the host. So,
55
+ # we have to re-calculate its value. We will remove this limitation
56
+ # when we execute the whole building from inside the Docker container
57
+ # (instead behing a hybrid as it is now)
58
+ #
59
+ # We need to have two different paths that point to the exact same
60
+ # directory. How is that? The directory is mounted into a different
61
+ # location inside the container:
62
+ #
63
+ # 1. path in the host:
64
+ # /home/docs/checkouts/readthedocs.org/user_builds/<project>/
65
+ # 2. path in the container:
66
+ # /usr/src/app/checkouts/readthedocs.org/user_builds/b9cbc24c8841/test-builds/
67
+ #
68
+ # Besides, the variable `$READTHEDOCS_OUTPUT` is not defined in the
69
+ # host, so we have to expand it using the full host's path. This
70
+ # variable cannot be used in cwd= due to a limitation of the Docker API
71
+ # (I guess) since I received an error when trying that. So, we have to
72
+ # fully expand it.
73
+ #
74
+ # That said, we need:
75
+ #
76
+ # * use the path in the host, for all the operations that are done via
77
+ # Python from the app (e.g. os.path.join, glob.glob, etc)
78
+ #
79
+ # * use the path in the container, for all the operations that are
80
+ # executed inside the container via Docker API using shell commands
81
+ self .absolute_host_output_dir = os .path .join (
82
+ os .path .join (
83
+ self .project .checkout_path (self .version .slug ),
84
+ "_readthedocs/" ,
85
+ ),
86
+ self .relative_output_dir ,
87
+ )
88
+ self .absolute_container_output_dir = os .path .join (
89
+ "$READTHEDOCS_OUTPUT" , self .relative_output_dir
54
90
)
91
+
55
92
try :
56
93
if not self .config_file :
57
94
self .config_file = self .project .conf_file (self .version .slug )
@@ -309,9 +346,7 @@ def build(self):
309
346
# https://github.com/readthedocs/readthedocs.org/pull/9888#issuecomment-1384649346
310
347
"." ,
311
348
# Sphinx's output build directory (OUTPUTDIR)
312
- os .path .relpath (
313
- self .absolute_output_dir , os .path .dirname (self .config_file )
314
- ),
349
+ self .absolute_container_output_dir ,
315
350
]
316
351
)
317
352
cmd_ret = self .run (
@@ -338,7 +373,7 @@ def sphinx_parallel_arg(self):
338
373
339
374
340
375
class HtmlBuilder (BaseSphinx ):
341
- relative_output_dir = "_readthedocs/ html"
376
+ relative_output_dir = "html"
342
377
343
378
def __init__ (self , * args , ** kwargs ):
344
379
super ().__init__ (* args , ** kwargs )
@@ -367,18 +402,16 @@ def __init__(self, *args, **kwargs):
367
402
368
403
class LocalMediaBuilder (BaseSphinx ):
369
404
sphinx_builder = 'readthedocssinglehtmllocalmedia'
370
- relative_output_dir = "_readthedocs/ htmlzip"
405
+ relative_output_dir = "htmlzip"
371
406
372
407
def _post_build (self ):
373
408
"""Internal post build to create the ZIP file from the HTML output."""
374
- target_file = os .path .abspath (
375
- os .path .join (
376
- self .absolute_output_dir ,
377
- # TODO: shouldn't this name include the name of the version as well?
378
- # It seems we were using the project's slug previously.
379
- # So, keeping it like that for now until we decide make that adjustment.
380
- f"{ self .project .slug } .zip" ,
381
- )
409
+ target_file = os .path .join (
410
+ self .absolute_container_output_dir ,
411
+ # TODO: shouldn't this name include the name of the version as well?
412
+ # It seems we were using the project's slug previously.
413
+ # So, keeping it like that for now until we decide make that adjustment.
414
+ f"{ self .project .slug } .zip" ,
382
415
)
383
416
384
417
# **SECURITY CRITICAL: Advisory GHSA-hqwg-gjqw-h5wg**
@@ -390,15 +423,15 @@ def _post_build(self):
390
423
dirname = f"{ self .project .slug } -{ self .version .slug } "
391
424
self .run (
392
425
"mv" ,
393
- self .relative_output_dir ,
426
+ self .absolute_container_output_dir ,
394
427
str (tmp_dir / dirname ),
395
428
cwd = self .project_path ,
396
429
record = False ,
397
430
)
398
431
self .run (
399
432
"mkdir" ,
400
433
"--parents" ,
401
- self .relative_output_dir ,
434
+ self .absolute_container_output_dir ,
402
435
cwd = self .project_path ,
403
436
record = False ,
404
437
)
@@ -416,17 +449,19 @@ def _post_build(self):
416
449
class EpubBuilder (BaseSphinx ):
417
450
418
451
sphinx_builder = "epub"
419
- relative_output_dir = "_readthedocs/ epub"
452
+ relative_output_dir = "epub"
420
453
421
454
def _post_build (self ):
422
455
"""Internal post build to cleanup EPUB output directory and leave only one .epub file."""
423
456
temp_epub_file = f"/tmp/{ self .project .slug } -{ self .version .slug } .epub"
424
457
target_file = os .path .join (
425
- self .absolute_output_dir ,
458
+ self .absolute_container_output_dir ,
426
459
f"{ self .project .slug } .epub" ,
427
460
)
428
461
429
- epub_sphinx_filepaths = glob (os .path .join (self .absolute_output_dir , "*.epub" ))
462
+ epub_sphinx_filepaths = glob (
463
+ os .path .join (self .absolute_host_output_dir , "*.epub" )
464
+ )
430
465
if epub_sphinx_filepaths :
431
466
# NOTE: we currently support only one .epub per version
432
467
epub_filepath = epub_sphinx_filepaths [0 ]
@@ -437,14 +472,14 @@ def _post_build(self):
437
472
self .run (
438
473
"rm" ,
439
474
"--recursive" ,
440
- self .relative_output_dir ,
475
+ self .absolute_container_output_dir ,
441
476
cwd = self .project_path ,
442
477
record = False ,
443
478
)
444
479
self .run (
445
480
"mkdir" ,
446
481
"--parents" ,
447
- self .relative_output_dir ,
482
+ self .absolute_container_output_dir ,
448
483
cwd = self .project_path ,
449
484
record = False ,
450
485
)
@@ -481,7 +516,7 @@ class PdfBuilder(BaseSphinx):
481
516
482
517
"""Builder to generate PDF documentation."""
483
518
484
- relative_output_dir = "_readthedocs/ pdf"
519
+ relative_output_dir = "pdf"
485
520
sphinx_builder = "latex"
486
521
pdf_file_name = None
487
522
@@ -504,14 +539,12 @@ def build(self):
504
539
# https://github.com/readthedocs/readthedocs.org/pull/9888#issuecomment-1384649346
505
540
"." ,
506
541
# Sphinx's output build directory (OUTPUTDIR)
507
- os .path .relpath (
508
- self .absolute_output_dir , os .path .dirname (self .config_file )
509
- ),
542
+ self .absolute_container_output_dir ,
510
543
cwd = os .path .dirname (self .config_file ),
511
544
bin_path = self .python_env .venv_bin (),
512
545
)
513
546
514
- tex_files = glob (os .path .join (self .absolute_output_dir , "*.tex" ))
547
+ tex_files = glob (os .path .join (self .absolute_host_output_dir , "*.tex" ))
515
548
if not tex_files :
516
549
raise BuildUserError ("No TeX files were found." )
517
550
@@ -526,7 +559,7 @@ def _build_latexmk(self, cwd):
526
559
# https://github.com/sphinx-doc/sphinx/blob/master/sphinx/texinputs/Makefile_t
527
560
images = []
528
561
for extension in ("png" , "gif" , "jpg" , "jpeg" ):
529
- images .extend (Path (self .absolute_output_dir ).glob (f"*.{ extension } " ))
562
+ images .extend (Path (self .absolute_host_output_dir ).glob (f"*.{ extension } " ))
530
563
531
564
# FIXME: instead of checking by language here, what we want to check if
532
565
# ``latex_engine`` is ``platex``
@@ -536,13 +569,13 @@ def _build_latexmk(self, cwd):
536
569
# step. I don't know exactly why but most of the documentation that
537
570
# I read differentiate this language from the others. I suppose
538
571
# it's because it mix kanji (Chinese) with its own symbols.
539
- pdfs = Path (self .absolute_output_dir ).glob ("*.pdf" )
572
+ pdfs = Path (self .absolute_host_output_dir ).glob ("*.pdf" )
540
573
541
574
for image in itertools .chain (images , pdfs ):
542
575
self .run (
543
576
'extractbb' ,
544
577
image .name ,
545
- cwd = self .absolute_output_dir ,
578
+ cwd = self .absolute_host_output_dir ,
546
579
record = False ,
547
580
)
548
581
@@ -553,7 +586,7 @@ def _build_latexmk(self, cwd):
553
586
self .run (
554
587
'cat' ,
555
588
rcfile ,
556
- cwd = self .absolute_output_dir ,
589
+ cwd = self .absolute_host_output_dir ,
557
590
)
558
591
559
592
if self .build_env .command_class == DockerBuildCommand :
@@ -581,7 +614,7 @@ def _build_latexmk(self, cwd):
581
614
cls = latex_class ,
582
615
cmd = cmd ,
583
616
warn_only = True ,
584
- cwd = self .absolute_output_dir ,
617
+ cwd = self .absolute_host_output_dir ,
585
618
)
586
619
587
620
self .pdf_file_name = f'{ self .project .slug } .pdf'
@@ -597,13 +630,19 @@ def _post_build(self):
597
630
# TODO: merge this with ePUB since it's pretty much the same
598
631
temp_pdf_file = f"/tmp/{ self .project .slug } -{ self .version .slug } .pdf"
599
632
target_file = os .path .join (
600
- self .absolute_output_dir ,
633
+ self .absolute_container_output_dir ,
601
634
self .pdf_file_name ,
602
635
)
603
636
604
637
# NOTE: we currently support only one .pdf per version
605
- pdf_sphinx_filepath = os .path .join (self .absolute_output_dir , self .pdf_file_name )
606
- if os .path .exists (pdf_sphinx_filepath ):
638
+ pdf_sphinx_filepath = os .path .join (
639
+ self .absolute_container_output_dir , self .pdf_file_name
640
+ )
641
+ pdf_sphinx_filepath_host = os .path .join (
642
+ self .absolute_host_output_dir ,
643
+ self .pdf_file_name ,
644
+ )
645
+ if os .path .exists (pdf_sphinx_filepath_host ):
607
646
self .run (
608
647
"mv" ,
609
648
pdf_sphinx_filepath ,
@@ -614,14 +653,14 @@ def _post_build(self):
614
653
self .run (
615
654
"rm" ,
616
655
"-r" ,
617
- self .relative_output_dir ,
656
+ self .absolute_container_output_dir ,
618
657
cwd = self .project_path ,
619
658
record = False ,
620
659
)
621
660
self .run (
622
661
"mkdir" ,
623
662
"-p" ,
624
- self .relative_output_dir ,
663
+ self .absolute_container_output_dir ,
625
664
cwd = self .project_path ,
626
665
record = False ,
627
666
)
0 commit comments