diff --git a/.smpte-build.json b/.smpte-build.json index a49fdb3..c46e10f 100644 --- a/.smpte-build.json +++ b/.smpte-build.json @@ -1,3 +1,3 @@ { - "latestEditionTag": "20260319-pub" + "latestEditionTag": null } diff --git a/doc/main.html b/doc/main.html index bb206a0..2b1b5d0 100644 --- a/doc/main.html +++ b/doc/main.html @@ -9,8 +9,8 @@ - - + + @@ -46,6 +46,8 @@ https://doc.smpte-doc.org/ag-07/main/
  • SMPTE AG 31, GitHub Operating Guidelines https://doc.smpte-doc.org/ag-31/main/
  • +
  • SMPTE AG 33, Document Process and Workflow + https://doc.smpte-doc.org/ag-33/main/
  • @@ -246,7 +248,7 @@

    pubState

    - For further explanation of determining the value of the document's pubState, see + For further explanation of determining the value of the document's pubState, see

    @@ -270,7 +272,7 @@

    pubStage

    - For further explanation of determining the value of the document's pubStage, see + For further explanation of determining the value of the document's pubStage, see

    @@ -1639,23 +1641,25 @@

    Building

    The S3 upload step can be skipped by providing the validate argument to the build script.

    -

    The build process can be configured using the .smpte-build.json file which is contains a JSON object that - conforms to the JSON schema at .

    +

    The build process MAY be configured using an optional .smpte-build.json file containing a JSON object that + conforms to the JSON schema at . When the file is absent, the build derives + its configuration from the document metadata and the repository's git tags.

    
    -        
    Schema for the build configuration file .smpte-build.json.
    +
    Schema for the optional build configuration file .smpte-build.json.
    -

    If present or not equal to null, the latestEditionTag property contains the Git tag or commit - that is used when generating a redline against the latest published edition of the document.

    +

    If present and not equal to null, the latestEditionTag property overrides the auto-selected + base for the published-edition redline with the specified Git tag or commit. When absent or null, the base is + auto-selected as described in .

    
           

    - For further explanation of determining the value of latestEditionTag, see + For the complete description of redline outputs produced by the tooling, see .

    @@ -1696,6 +1700,39 @@

    GitHub integration

    +
    +

    Redlines

    + +

    The tooling automatically produces redline (change-tracked) renderings of the document on every build — pull requests, merges to main, and release publications. Redlines compare the current rendered document against a chosen reference rendering using the htmldiff utility, and the resulting HTML files are deployed to S3 alongside the clean output and bundled into the review zip when applicable.

    + +

    Three redlines may be produced per build, each with a different reference base:

    + +
    +
    Redline to current draft (base-rl.html)
    +
    +

    Generated against the pull request's target branch (typically main), via the GITHUB_BASE_REF environment variable provided by GitHub Actions on pull request events. Shows what the PR itself changes relative to the branch it will merge into.

    +

    Generated: on pull request events. Omitted: on push and release events, where no PR base is defined.

    +
    + +
    Redline to most recent published edition (pub-rl.html)
    +
    +

    Generated against the most recent prior git tag matching YYYYMMDD-pub — the previous published edition. Shows what has changed since the last edition was published. The reference base may be overridden by setting latestEditionTag in .smpte-build.json — useful when rebuilding an archived edition or pinning to a specific historical release.

    +

    This redline satisfies the requirement in for a redline against the published document (the baseline draft) when preparing a revision.

    +

    Generated: on every event. Omitted: when no prior -pub tag exists (e.g., the first edition).

    +
    + +
    Redline to most recent release (release-rl.html)
    +
    +

    Generated against the most recent prior git tag matching YYYYMMDD-{stage} for any recognized pubStage (-wd, -cd, -fcd, -dp, or -pub). Shows iterative changes since the last release tag of any kind, supporting draft-to-draft review across the workflow stages defined in .

    +

    This redline satisfies the requirement in that markup from the ballot document be provided for comment resolution.

    +

    Generated: when the current document's pubStage is WD, CD, FCD, or DP. Omitted: when pubStage is PUB (a published edition's relevant comparison is to the prior published edition only) or when no prior dated release tag exists.

    +
    +
    + +

    In all cases, any tag pointing at the HEAD commit is excluded from selection, so a freshly-tagged release does not redline against itself. When the build runs on a pull request, links to all available redline outputs are included in the comment posted to the PR (see ). When the build runs on a release publication, the redline files are bundled into the review zip attached to the release (see ).

    + +
    +

    Amazon AWS integration

    @@ -2664,377 +2701,50 @@

    Unordered List Without Bullets

    -

    Document Status Workflow

    - -
    -

    Mapping meta tags to Document Status

    -

    - maps the required content values of pubState, pubStage, and pubConfidential during the various stages of document development. These values represent the naming convention(s) as defined in and flowcharts as defined in -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Mapping content values for meta tags to Document Status
    Document StatuspubStage valuepubState valuepubConfidential value
    Initial draft or revision (Working Draft) in: -
    Draft Group (DG) -
    Study Group (SG) -
    Working Group (WG)
    WDdraftyes
    >> Document moves to TC >>
    Pre-FCD (Final Committee Draft) reviewWDpubyes
    >> If pre-FCD comments received, back to DG/SG/WG >>
    Pre-FCD comment resolution ongoing CDdraftyes
    Pre-FCD comment resolution complete CDpubyes
    >> Document moves to TC >>
    PCD (Public Committee Draft) periodCDpubno
    PCD comment resolution ongoing (moves back to DG/SG/WG)CDdraftyes
    PCD comment resolution complete>> Back to Pre-FCD (Final Committee Draft) review >>
    (OR)
    Ready for FCD ballotCDpubyes
    FCD ballot periodCDpubyes
    >> If FCD ballot comments received >>
    FCD ballot comment resolution ongoingFCDdraftyes
    FCD ballot comment resolution completeFCDpubyes
    Pre-DP (Draft Publication) reviewFCDpubyes
    Pre-DP ballot periodFCDpubyes
    (ELSE)
    FCD ballot passes, no commentsDPpubyes
    DP ballot passesDPpubyes
    Ready for ST (Standards Committee) AuditDPpubyes
    ST Audit periodDPpubyes
    ST Audit completePUBdraftyes
    Waiting for publicationPUBdraftyes
    PublishedPUBpubno
    - -

    - See for a sample workflow utilizing these statuses. -

    +

    Auto-draft releases

    +
    +

    General

    +

    This annex describes the tooling behavior that auto-drafts GitHub releases. For the document workflow that drives when releases are created and who publishes them, see .

    -
    -

    GitHub Releases

    -

    - Whenever the state of the document changes to <meta itemprop="pubState" content="pub" /> on the main branch, then a release shall be created in the GitHub repo. -

    -

    - Both the GitHub release and git tag shall be named YYYYMMDD "-" pubStage. The YYYYMMDD should directly represent the value used in pubDateTime as defined in . -

    -

    - 20230615-fcd or 20230925-pub -

    -

    - The latestEditionTag property in the .smpte-build.json file shall always be equal to the name of the most recent git tag. This will trigger the tooling to create a link for Redline to most recent edition during any PRs and merges to the main branch. -

    -

    - The tooling will automatically generate zip file packages within the release details page, to be used for both :

    -
      -
    • TC review/balloting (named as per )
    • -
    • (and) SMPTE Document Library publishing (named by the tag)
    • -
    - -

    See https://github.com/SMPTE/st428-24-private/releases/tag/20241101-dp, where https://github.com/SMPTE/st428-24-private/releases/download/20241101-dp/27c-st-428-24-dp-2024-12-06-pub.zip (ballot zip), and https://github.com/SMPTE/st428-24-private/releases/download/20241101-dp/20241101-dp.zip (publish zip) are available.

    - -

    - See for a sample workflow utilizing releases. -

    - +
    +

    Trigger and auto-draft

    +

    Whenever the state of the document changes to <meta itemprop="pubState" content="pub" /> on the main branch, the tooling automatically creates a draft release in the GitHub repo. A user with appropriate permissions reviews and publishes the draft to finalize the release and trigger production of the associated artifacts.

    -
    -

    Pull Request Guidelines

    - -

    - Generally, no changes to a document shall ever be made directly on the main branch, which doesn't allow the tooling to create needed redlines. Instead, changes (regardless of the nature of the change) shall always be made on a new branch and managed via a PR (Pull Request). Each PR is then merged to main after approval, as noted below in at the various stages. -

    -

    PRs should have at least (1) approver that is not the person requesting the PR. This can be the DG chair, TC chair, commentor, or secondary editor, depending on nature of the PR. For instance, on ballot state change PRs, this should be the DG or TC chair, and for publication state PRs, the TC chairs. See for more details.

    +
    +

    Tag and release naming

    +

    The GitHub release and the git tag shall be named YYYYMMDD "-" pubStage, where YYYYMMDD represents the value of pubDateTime (as defined in ) and the pubStage suffix is the value of the pubStage meta in lowercase.

    +

    20230615-fcd or 20230925-pub

    +
    +
    +

    Same-day iterations

    +

    For workflow events that share both pubDateTime and pubStage with the most recent release but represent a distinct release moment, the merge commit message shall include the token [force-release]. The tooling appends a numeric suffix (-2, -3, …) to the tag to disambiguate.

    -
    -

    Sample Workflow

    -

    - The below list shows a sample workflow which would be the steps for an Engineering Document going through an initial draft or revision and the balloting process. -

    - -

    Editors and Chairs should be aware of the general guidelines provided in the . -

    +
    +

    Publishing the draft release

    +

    To publish a draft release after it has been auto-created:

      - -
    1. Project is approved -
        -
      1. If document is an initial draft, skip to Workflow Step (2)
      2. -
      3. If document is a revision, skip to Workflow Step (3)
      4. -
      -
    2. - -
    3. Project is a document initial draft -
        -
      1. Follow the steps in to create repo.
      2. -
      3. A new branch is created from main called 2023-initial-draft (where "2023" is the current year).
      4. -
      5. Make sure that the pubState () and pubStage () of the main prose element in the 2023-initial-draft branch are set to - draft and WD, respectively.
      6. -
      7. Document editor creates initial version of the document.
      8. -
      9. A PR is created named 2023 Initial Draft from the 2023-initial-draft branch on GitHub.
      10. -
      11. Skip to Workflow Step (4)
      12. -
      -
    4. - -
    5. Project is a document revision -
        -
      1. Assumption: There is a prior release labeled 2020115-pub
      2. -
      3. A new branch is created from main called 2023-revision (where "2023" is the current year).
      4. -
      5. Make sure that the pubState () and pubStage () of the main prose element in the 2023-initial-draft branch are set to - draft and WD, respectively.
      6. -
      7. .smpte-build.json is updated in 2023-revision branch to last release tag - 2020115-pub (see )
      8. -
      9. Document editor makes initial changes to the document.
      10. -
      11. A PR is created named 2023 Revision from the 2023-revision branch on GitHub. The tooling with create a redline against the most recent edition of the document.
      12. -
      13. Continue to Workflow Step (4)
      14. -
      -
    6. - -
    7. DG meeting(s) held regarding drafting of the document -
        -
      1. Comments are received, either by reflector or during meeting(s).
      2. -
      3. Comments and suggested changes are added to issue tracker.
      4. -
      5. Document is updated via more commits in the current PR, closing comments.
      6. -
      7. Continue to Workflow Step (5)
      8. -
      -
    8. - -
    9. DG is complete with drafting the document -
        -
      1. Document is updated in PR to <meta itemprop="pubState" content="pub" />, <meta itemprop="pubStage" content="WD" /> and <meta itemprop="pubDateTime" content="2023-04-15" /> (assumed date of completion).
      2. -
      3. PR is approved by document editor and DG chair. PR is merged to main.
      4. -
      5. A release is created targeted at main named 20230415-wd with a tag of the same name.
      6. -
      7. The tooling creates the zip needed for TC 2-week preFCD review, with redlines against latest edition and/or last draft (if applicable)
      8. -
      9. The link to the ZIP is sent to TC for 2-week preFCD review
      10. -
      11. Continue to Workflow Step (6)
      12. -
      -
    10. - -
    11. TC 2-week preFCD review occurs -
        -
      1. A new branch is created from main called 20230415-cd1.
      2. -
      3. Document is updated in 20230415-cd1 branch to <meta itemprop="pubState" content="draft" /> and <meta itemprop="pubStage" content="CD" />.
      4. -
      5. .smpte-build.json is updated in 20230415-cd1 branch to 20230415-wd tag
      6. -
      7. A PR is created named 20230415 CD1 from the 20230415-cd1 branch on GitHub.
      8. -
      9. If no comments received, skip to Workflow Step (8)
      10. -
      11. If comments are received, continue to Workflow Step (7)
      12. -
      -
    12. - -
    13. Comments received on pre-FCD ballot review -
        -
      1. Document is updated in PR from Workflow Step (6) as needed per comments received. A redline from last draft is created by the tooling as required to resolve comments.
      2. -
      3. Once comments are resolved he document is updated in PR to <meta itemprop="pubDateTime" content="2023-05-15" /> (assumed date of completion).
      4. -
      5. Continue to Workflow Step (8)
      6. -
      -
    14. - -
    15. Document is ready for FCD ballot -
        -
      1. Document is updated in PR from Workflow Step (6) to <meta itemprop="pubState" content="pub" /> -
          -
        • If document did NOT receive any comments, the pubDateTime remains unchanged from Workflow Step (5).
        • -
        -
      2. -
      3. PR is merged to main.
      4. -
      5. A release is created targeted at main named 20230415-cd1 (or 20230515-cd1 if Workflow Step 7 was used) with a tag of the same name.
      6. -
      7. If document is to continue to FCD ballot, the link to the ZIP file that is generated by the tooling in main with required redlines is sent to the TC for FCD ballot. Skip to Workflow Step (9)
      8. -
      9. If document is to undergo a PCD period, the contents in the ZIP file generated by the tooling in main are used for the PCD. After PCD period ends, go back to Workflow Step (6). See for iterative CD naming.
      10. -
      11. Continue to Workflow Step (9)
      12. -
      -
    16. - -
    17. TC conducts FCD ballot -
        -
      1. If no comments received, skip to Workflow Step (12)
      2. -
      3. If comments are received, continue to Workflow Step (10)
      4. -
      -
    18. - -
    19. FCD ballot comment resolution -
        -
      1. A new branch is created from main called 2023-revision-fcd-comment-res.
      2. -
      3. Document is updated in 2023-revision-fcd-comment-res branch to <meta itemprop="pubState" content="draft" /> and <meta itemprop="pubStage" content="FCD" />.
      4. -
      5. .smpte-build.json is updated in 2023-revision-fcd-comment-res branch to 20230415-cd1 (or 20230515-cd1) tag
      6. -
      7. PR is created on GitHub with the changes in 2023-revision-fcd-comment-res as needed to resolve comments. The tooling creates redline for review against the last edition (release 20230415-cd1 or 20230515-cd1).
      8. -
      9. Additional commits are added to the PR as need to resolve comments.
      10. -
      11. Comment resolution ends, document is updated to <meta itemprop="pubState" content="pub" /> and <meta itemprop="pubDateTime" content="2023-07-15" /> (assumed date of completion).
      12. -
      13. PR is approved by document editor and DG chair. PR is merged to main.
      14. -
      15. A release is created targeted at main named 20230715-fcd with a tag of the same name.
      16. -
      17. The link to the ZIP file that is generated by the tooling in main is sent to the TC for pre-DP review.
      18. -
      19. Continue to Workflow Step (11)
      20. -
      -
    20. - -
    21. TC conducts DP ballot -
        -
      1. DP ballot passes, continue to Workflow Step (12)
      2. -
      -
    22. - -
    23. Document is now DP -
        -
      1. A new branch is created from main called 20230415-dp (or 20230715-dp if Workflow Step 5 was used or 20230715-dp if Workflow Step 10 was used).
      2. -
      3. Document is updated in 20230415-dp branch to <meta itemprop="pubStage" content="DP" />
      4. -
      5. .smpte-build.json is updated in 20230415-dp branch to 20230415-cd1 tag (or 20230515-cd1)
      6. -
      7. PR is created on GitHub with the changes as per needed to resolve comments. The tooling creates redline for review against the last edition (release 20230415-cd1 or 20230515-cd1 or 20230715-fcd).
      8. -
      9. PR is approved by document editor and DG chair. PR is merged to main.
      10. -
      11. A release is created targeted at main named 20230415-dp (or 20230515-dp or 20230715-dp) with a tag of the same name.
      12. -
      13. The link to the ZIP file that is generated by the tooling in main is sent to DoS for ST audit.
      14. -
      15. Continue to Workflow Step (13)
      16. -
      -
    24. - -
    25. ST conducts ST audit -
        -
      1. ST audit passes, continue to Workflow Step (14)
      2. -
      -
    26. - -
    27. Document is ready for publication -
        -
      1. A new branch is created from main called 20230415-pub (or 20230515-pub or 20230715-pub).
      2. -
      3. Document is updated in 20230415-pub branch to <meta itemprop="pubStage" content="pub" />
      4. -
      5. If document is a revision, .smpte-build.json is updated in 20230415-pub branch to 2020115-pub tag
      6. -
      7. PR is created on GitHub from 20230415-pub
      8. -
      9. PR is approved by document editor, DG chair, and TC chair(s). PR is merged to main.
      10. -
      11. A release is created targeted at main named 20230415-pub (or 20230515-pub or 20230715-pub) with a tag of the same name.
      12. -
      13. The document is now "published" on main.
      14. -
      15. Send SMPTE HO the 20230415-pub.zip (or 20230515-pub.zip or 20230715-pub.zip) generated in the release to use in the https://github.com/SMPTE/document-library. -
          -
        • Automation for this step is planned for a future date.
        • -
        -
      16. -
      -
    28. +
    29. In the repository on GitHub, navigate to the Releases page (https://github.com/SMPTE/{repo}/releases).
    30. +
    31. Open the draft release whose name matches the expected YYYYMMDD-pubStage tag for the merged document state.
    32. +
    33. Click Edit, review the auto-generated release notes, and confirm that the target commit corresponds to the merge that produced the new state.
    34. +
    35. Click Publish release.
    +
    +
    +

    Release artifacts

    +

    Publishing the draft release attaches zip packages to the release details page:

    +
      +
    • Ballot zip — named per . Bundles the rendered document together with the published-edition redline (pub-rl.html) and the most-recent-release redline (release-rl.html). See for redline details and reference-base selection.
    • +
    • Publish zip — named by the tag, used for publishing per .
    • +
    +

    See https://github.com/SMPTE/st428-24-private/releases/tag/20241101-dp, where https://github.com/SMPTE/st428-24-private/releases/download/20241101-dp/27c-st-428-24-dp-2024-12-06-pub.zip (ballot zip) and https://github.com/SMPTE/st428-24-private/releases/download/20241101-dp/20241101-dp.zip (publish zip) are available.

    +
    diff --git a/json/schema/build-configuration.json b/json/schema/build-configuration.json index 78968af..94c73c0 100644 --- a/json/schema/build-configuration.json +++ b/json/schema/build-configuration.json @@ -4,7 +4,7 @@ "type": "object", "properties": { "latestEditionTag": { - "description": "Git commit or tag used when generating redlines against the latest edition of the document", + "description": "Optional override for the redline base. When set, the build uses this git commit or tag when generating redlines against the latest edition. When unset or null, the build auto-derives the redline base from the most recent matching git tag (most recent prior `-pub` tag when the document's pubStage is PUB; otherwise the most recent prior dated tag of any stage).", "type": [ "string", "null" ] } } diff --git a/scripts/build.mjs b/scripts/build.mjs index 5b8de88..6f190ad 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -157,7 +157,7 @@ function mirrorDirExcludeTooling(srcDir, targetDir, relParentPath) { } -async function build(buildPaths, baseRef, lastEdRef, docMetadata) { +async function build(buildPaths, baseRef, lastEdRef, lastReleaseRef, docMetadata) { const generatedFiles = {}; @@ -197,19 +197,34 @@ async function build(buildPaths, baseRef, lastEdRef, docMetadata) { } - /* generate pub redline, if requested */ - - if (lastEdRef !== null) { + /* fetch tags once for both pub and release redlines */ + if (lastEdRef !== null || lastReleaseRef !== null) { child_process.execSync(`git fetch --tags`); + } + + /* generate redline against most recent published edition, if requested */ + + if (lastEdRef !== null) { - console.log(`Generating a redline against the latest edition tag: ${lastEdRef}.`); + console.log(`Generating a redline against the latest published edition tag: ${lastEdRef}.`); await generateRedline(buildPaths, lastEdRef, buildPaths.pubRedLineRefPath, buildPaths.pubRedlinePath); generatedFiles.pubRedline = buildPaths.pubRedlineName; } + /* generate redline against most recent release tag, if requested */ + + if (lastReleaseRef !== null) { + + console.log(`Generating a redline against the latest release tag: ${lastReleaseRef}.`); + + await generateRedline(buildPaths, lastReleaseRef, buildPaths.releaseRedLineRefPath, buildPaths.releaseRedlinePath); + generatedFiles.releaseRedline = buildPaths.releaseRedlineName; + + } + return generatedFiles; } @@ -245,7 +260,10 @@ async function generatePubLinks(buildPaths, pubLinks) { linksDocContents += `[Redline to current draft](${pubLinks.baseRedline})\n`; if ("pubRedline" in pubLinks) - linksDocContents += `[Redline to most recent edition](${pubLinks.pubRedline})\n`; + linksDocContents += `[Redline to most recent published edition](${pubLinks.pubRedline})\n`; + + if ("releaseRedline" in pubLinks) + linksDocContents += `[Redline to most recent release](${pubLinks.releaseRedline})\n`; if ("reviewZip" in pubLinks) linksDocContents += `[ZIP package](${pubLinks.reviewZip})\n`; @@ -297,6 +315,10 @@ async function s3Upload(buildPaths, versionKey, generatedFiles) { pubLinks.pubRedline = `${deployPrefix}${s3PubKeyPrefix}${encodeURIComponent(generatedFiles.pubRedline)}`; } + if ("releaseRedline" in generatedFiles) { + pubLinks.releaseRedline = `${deployPrefix}${s3PubKeyPrefix}${encodeURIComponent(generatedFiles.releaseRedline)}`; + } + if ("reviewZip" in generatedFiles) { pubLinks.reviewZip = `${deployPrefix}${s3PubKeyPrefix}${encodeURIComponent(generatedFiles.reviewZip)}`; } @@ -334,6 +356,9 @@ async function makeReviewZip(buildPaths, generatedFiles, docMetadata) { if ("pubRedline" in generatedFiles) zip.addLocalFile(path.join(buildPaths.pubDirPath, generatedFiles.pubRedline)); + if ("releaseRedline" in generatedFiles) + zip.addLocalFile(path.join(buildPaths.pubDirPath, generatedFiles.releaseRedline)); + /* create zip filename */ const comps = []; @@ -383,7 +408,11 @@ async function makePubArtifacts(buildPaths, generatedFiles, docMetadata) { } if ("pubRedline" in generatedFiles) { - htmlLinks += `

    Redline to most recent edition

    \n`; + htmlLinks += `

    Redline to most recent published edition

    \n`; + } + + if ("releaseRedline" in generatedFiles) { + htmlLinks += `

    Redline to most recent release

    \n`; } if (generatedFiles.reviewZip !== undefined) { @@ -655,6 +684,10 @@ class BuildPaths { this.pubRedlinePath = path.join(this.pubDirPath, this.pubRedlineName); this.pubRedLineRefPath = path.join(this.buildDirPath, this.pubRedlineName); + this.releaseRedlineName = "release-rl.html"; + this.releaseRedlinePath = path.join(this.pubDirPath, this.releaseRedlineName); + this.releaseRedLineRefPath = path.join(this.buildDirPath, this.releaseRedlineName); + this.baseRedlineName = "base-rl.html"; this.baseRedlinePath = path.join(this.pubDirPath, this.baseRedlineName); this.baseRedLineRefPath = path.join(this.buildDirPath, this.baseRedlineName); @@ -671,13 +704,59 @@ class BuildConfig { try { config = JSON.parse(fs.readFileSync(path.join(docDirPath, ".smpte-build.json"))); } catch { - console.log("Could not read the publication config file."); + /* file is optional; auto-derivation is used when it is absent */ } - this.lastEdRef = config.latestEditionTag || null; + this.latestEditionTag = config.latestEditionTag || null; } } +const DATED_TAG_RE = /^\d{8}-(pub|fcd|cd|wd|dp)(-\d+)?$/; +const PUB_TAG_RE = /^\d{8}-pub(-\d+)?$/; + +function getHeadTags() { + try { + return new Set( + child_process.execSync("git tag --points-at HEAD") + .toString() + .split("\n") + .map(t => t.trim()) + .filter(Boolean) + ); + } catch { + return new Set(); + } +} + +function pickLatestTag(globs, filterRe) { + let allTags; + try { + allTags = child_process.execSync(`git tag -l ${globs.map(g => `'${g}'`).join(" ")} --sort=-version:refname`) + .toString() + .split("\n") + .map(t => t.trim()) + .filter(t => filterRe.test(t)); + } catch { + return null; + } + + const headTags = getHeadTags(); + return allTags.find(t => !headTags.has(t)) || null; +} + +function deriveLastEdRef(override) { + if (override !== null && override !== undefined && override !== "") { + return override; + } + return pickLatestTag(["*-pub*"], PUB_TAG_RE); +} + +function deriveLastReleaseRef(pubStage) { + /* a -pub release only needs the redline to the prior published edition */ + if (pubStage === "PUB") return null; + return pickLatestTag(["*-pub*", "*-fcd*", "*-cd*", "*-wd*", "*-dp*"], DATED_TAG_RE); +} + async function main() { /* retrieve build phase */ @@ -802,7 +881,10 @@ async function main() { /* render document */ - Object.assign(generatedFiles, await build(buildPaths, baseRef, config.lastEdRef, docMetadata)); + const lastEdRef = deriveLastEdRef(config.latestEditionTag); + const lastReleaseRef = deriveLastReleaseRef(docMetadata.pubStage); + + Object.assign(generatedFiles, await build(buildPaths, baseRef, lastEdRef, lastReleaseRef, docMetadata)); /* validate rendered document */ diff --git a/scripts/derive-release-tag.mjs b/scripts/derive-release-tag.mjs new file mode 100644 index 0000000..8f812bf --- /dev/null +++ b/scripts/derive-release-tag.mjs @@ -0,0 +1,81 @@ +/* (c) Society of Motion Picture and Television Engineers + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +import * as fs from "fs"; +import process from "process"; +import { argv } from "process"; + +const PUB_STAGES = new Set(["WD", "CD", "FCD", "DP", "PUB"]); + +function extractMeta(html, name) { + const re = new RegExp(`]*itemprop=["']${name}["'][^>]*content=["']([^"']*)["']`, "i"); + const m = html.match(re); + if (m) return m[1]; + const reSwapped = new RegExp(`]*content=["']([^"']*)["'][^>]*itemprop=["']${name}["']`, "i"); + const m2 = html.match(reSwapped); + return m2 ? m2[1] : null; +} + +function emit(tag) { + if (process.env.GITHUB_OUTPUT) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `tag=${tag}\n`); + } + process.stdout.write(`tag=${tag}\n`); +} + +const docPath = argv[2] || "doc/main.html"; + +if (!fs.existsSync(docPath)) { + console.error(`Document not found: ${docPath}`); + process.exit(1); +} + +const html = fs.readFileSync(docPath, "utf8"); +const headEnd = html.search(/<\/head>/i); +const head = headEnd >= 0 ? html.slice(0, headEnd) : html; + +const pubState = extractMeta(head, "pubState"); +const pubDateTime = extractMeta(head, "pubDateTime"); +const pubStage = extractMeta(head, "pubStage"); + +if (pubState !== "pub") { + emit(""); + process.exit(0); +} + +if (pubDateTime === null || !/^\d{4}-\d{2}-\d{2}$/.test(pubDateTime)) { + console.error(`pubDateTime must be a full YYYY-MM-DD date to form a release tag. Got: ${pubDateTime}`); + process.exit(1); +} + +if (pubStage === null || !PUB_STAGES.has(pubStage)) { + console.error(`pubStage must be one of ${[...PUB_STAGES].join(", ")} to form a release tag. Got: ${pubStage}`); + process.exit(1); +} + +const tag = `${pubDateTime.replace(/-/g, "")}-${pubStage.toLowerCase()}`; +emit(tag); diff --git a/workflows/action.yml b/workflows/action.yml index 175a993..47ff715 100644 --- a/workflows/action.yml +++ b/workflows/action.yml @@ -47,6 +47,41 @@ runs: node ${GITHUB_ACTION_PATH}/../scripts/build.mjs deploy cat ./build/vars.txt > "$GITHUB_OUTPUT" + - name: Create draft release if pubState=pub on main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.GITHUB_TOKEN }} + run: | + TAG=$(node ${GITHUB_ACTION_PATH}/../scripts/derive-release-tag.mjs | grep '^tag=' | head -1 | cut -d= -f2) + if [[ -z "$TAG" ]]; then + echo "pubState != pub or metadata incomplete; skipping draft release." + exit 0 + fi + FORCE="" + if [[ "${{ github.event.head_commit.message }}" == *"[force-release]"* ]]; then + FORCE="1" + fi + FINAL_TAG="$TAG" + if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then + if [[ -z "$FORCE" ]]; then + echo "Tag ${TAG} already exists; no-op." + exit 0 + fi + FINAL_TAG="" + for i in $(seq 2 99); do + if ! git rev-parse "refs/tags/${TAG}-${i}" >/dev/null 2>&1; then + FINAL_TAG="${TAG}-${i}" + break + fi + done + if [[ -z "$FINAL_TAG" ]]; then + echo "Could not resolve a free suffix for ${TAG} after 99 attempts." >&2 + exit 1 + fi + fi + gh release create "$FINAL_TAG" --draft --title "$FINAL_TAG" --target "$GITHUB_SHA" --generate-notes + - name: Determine which pull request we are on uses: jwalton/gh-find-current-pr@v1 if: github.event_name == 'pull_request'