This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 6e078a61e5 Update scripts
6e078a61e5 is described below
commit 6e078a61e53c68b6878ca7b032b01513e2bc0037
Author: James Bognar <[email protected]>
AuthorDate: Sun Dec 28 11:44:30 2025 -0500
Update scripts
---
juneau-docs/docusaurus.config.ts | 7 +-
scripts/README-add-versioned-javadocs.md | 259 ---------------------
scripts/README-build-and-push.md | 20 +-
scripts/README-build-docs.md | 51 ++--
scripts/README-current-release.md | 55 +++++
scripts/README-maven-version.md | 53 +++++
scripts/README-prompt-pgp-passphrase.md | 45 ++++
scripts/README-release-docs-stage.md | 71 ++++++
scripts/README-release-docs.md | 67 ++++++
scripts/README-release.md | 134 +++++++++++
scripts/README-revert-staged.md | 46 ++++
scripts/README-revert-unstaged.md | 50 ++++
scripts/README-test.md | 108 +++++++++
scripts/add-versioned-javadocs.py | 228 ------------------
scripts/build-docs.py | 32 +--
scripts/release-docs-stage.py | 70 ++++++
scripts/{release-docs-stage.py => release-docs.py} | 236 +++++++++----------
17 files changed, 874 insertions(+), 658 deletions(-)
diff --git a/juneau-docs/docusaurus.config.ts b/juneau-docs/docusaurus.config.ts
index 18645bb3d7..4f14ede99a 100644
--- a/juneau-docs/docusaurus.config.ts
+++ b/juneau-docs/docusaurus.config.ts
@@ -102,10 +102,9 @@ const baseThemeConfig: Preset.ThemeConfig = {
position: 'right',
},
{
- // Use full URL to avoid Docusaurus routing it as an internal page
- // The javadocs are static files served from /javadocs/ directory
- // Environment-aware: uses SITE_URL env var or defaults to production
URL
- href: `${siteUrl}/javadocs/`,
+ // Use relative path with /index.html to ensure Docusaurus treats it
as a static file
+ // rather than trying to route it as an internal page
+ href: '/javadocs/index.html',
label: 'Javadocs',
position: 'right',
},
diff --git a/scripts/README-add-versioned-javadocs.md
b/scripts/README-add-versioned-javadocs.md
deleted file mode 100644
index 13992cfbad..0000000000
--- a/scripts/README-add-versioned-javadocs.md
+++ /dev/null
@@ -1,259 +0,0 @@
-# Add Versioned Javadocs Script
-
-## Overview
-
-`add-versioned-javadocs.py` adds versioned Javadocs to the static
documentation site, maintaining a history of API documentation for all releases.
-
-## What It Does
-
-The script performs these operations:
-
-1. **Validates Source** - Ensures the source directory contains valid Javadocs
-2. **Checks for Conflicts** - Prompts if the version already exists
-3. **Copies Javadocs** - Copies all Javadoc files to the versioned directory
-4. **Updates "Latest"** - Optionally creates/updates the "latest" link (with
`--make-latest`)
-5. **Creates Symlink** - Uses symlinks when possible, falls back to copying
-
-## Usage
-
-### Add a New Version
-
-```bash
-cd /Users/james.bognar/git/juneau
-python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
-```
-
-### Add and Make it the Latest
-
-```bash
-python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
--make-latest
-```
-
-### Add from External Location
-
-```bash
-python3 scripts/add-versioned-javadocs.py 9.1.0 /path/to/old/javadocs
-```
-
-## Arguments
-
-- **`version`** (required) - Version number (e.g., 9.2.0, 9.1.0, 8.2.1)
-- **`source_path`** (required) - Path to Javadocs directory (must contain
`index.html`)
-- **`--make-latest`** (optional) - Update the "latest" symlink to this version
-
-## Output Example
-
-```bash
-$ python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
--make-latest
-
-[INFO] Creating directory for version 9.2.0...
-[INFO] Copying javadocs from target/site/apidocs to
/Users/james.bognar/git/juneau/juneau-docs/static/javadocs/9.2.0...
-[INFO] Successfully added javadocs for version 9.2.0
-[INFO] Updating 'latest' to point to version 9.2.0...
-[INFO] Created symlink: latest -> 9.2.0
-
-================================================
-[INFO] Javadocs for version 9.2.0 successfully added!
-================================================
-
- Location: /Users/james.bognar/git/juneau/juneau-docs/static/javadocs/9.2.0
- URL (after deployment): /javadocs/9.2.0/
- Latest URL: /javadocs/latest/
-
-[INFO] Next steps:
- 1. Review the copied files: ls -lh
/Users/james.bognar/git/juneau/juneau-docs/static/javadocs/9.2.0
- 2. Test locally: python3 scripts/start-docusaurus.py
- 3. Visit http://localhost:3000/javadocs/
- 4. Commit the changes if everything looks good
- 5. Consider updating index.html to add this version to the version grid
-```
-
-## Version Overwrite
-
-If a version already exists, the script will prompt:
-
-```
-[WARN] Version 9.2.0 already exists at
/Users/james.bognar/git/juneau/juneau-docs/static/javadocs/9.2.0
-Do you want to overwrite it? (y/N)
-```
-
-- Type `y` or `yes` to overwrite
-- Type `n` or press Enter to abort
-
-## Directory Structure
-
-After adding versions:
-
-```
-juneau-docs/
-└── static/
- └── javadocs/
- ├── 8.2.1/ # Old release
- ├── 9.0.0/ # Previous release
- ├── 9.1.0/ # Previous release
- ├── 9.2.0/ # Current release
- └── latest -> 9.2.0 # Symlink to current (if --make-latest used)
-```
-
-## The "Latest" Link
-
-The `--make-latest` flag updates a special "latest" link that always points to
the most recent version.
-
-### Symlink (Preferred)
-
-On systems that support symlinks (macOS, Linux, Git for Windows):
-```
-latest -> 9.2.0
-```
-
-### Copy (Fallback)
-
-On systems that don't support symlinks (some Windows configurations):
-```
-latest/ (full copy of 9.2.0/)
-```
-
-**URL:** `http://localhost:3000/javadocs/latest/` always shows the current
version.
-
-## When to Use
-
-### New Release
-
-```bash
-# Step 1: Build project with Javadocs
-mvn clean install javadoc:javadoc
-
-# Step 2: Add versioned Javadocs
-python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
--make-latest
-
-# Step 3: Test locally
-python3 scripts/start-docusaurus.py
-# Visit http://localhost:3000/javadocs/9.2.0/
-
-# Step 4: Commit
-git add juneau-docs/static/javadocs/
-git commit -m "Added Javadocs for version 9.2.0"
-```
-
-### Adding Historical Versions
-
-If you have old Javadocs archived somewhere:
-
-```bash
-# Add old versions (don't make them latest)
-python3 scripts/add-versioned-javadocs.py 9.0.0 /path/to/archived/9.0.0/apidocs
-python3 scripts/add-versioned-javadocs.py 9.1.0 /path/to/archived/9.1.0/apidocs
-
-# Add current version as latest
-python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
--make-latest
-```
-
-### Updating Documentation Between Releases
-
-During development, you might regenerate Javadocs multiple times:
-
-```bash
-# Regenerate Javadocs
-mvn javadoc:javadoc
-
-# Update the SNAPSHOT version (overwrite)
-python3 scripts/add-versioned-javadocs.py 9.2.0-SNAPSHOT target/site/apidocs
--make-latest
-# Type 'y' when prompted to overwrite
-```
-
-## Validation
-
-The script validates the source before copying:
-
-1. **Directory Exists** - Source path must be a valid directory
-2. **Contains Javadocs** - Must have an `index.html` file
-3. **Not Empty** - Directory must contain actual content
-
-If validation fails, you'll see:
-```
-[ERROR] Source path does not exist: /path/to/nonexistent
-```
-
-or
-
-```
-[ERROR] Source path does not appear to contain javadocs (missing index.html)
-```
-
-## Troubleshooting
-
-### Source Path Not Found
-
-Make sure you've generated Javadocs first:
-
-```bash
-mvn javadoc:javadoc
-ls -la target/site/apidocs/index.html
-```
-
-### Permission Denied
-
-If you get permission errors:
-
-```bash
-# Fix permissions (macOS/Linux)
-sudo chown -R $USER:$USER juneau-docs/static/javadocs/
-
-# Or use sudo (not recommended)
-sudo python3 scripts/add-versioned-javadocs.py 9.2.0 target/site/apidocs
-```
-
-### Symlink Not Created
-
-On Windows or some file systems, symlinks might not work. The script
automatically falls back to copying:
-
-```
-[WARN] Symlinks not supported, copying instead...
-[INFO] Copied to 'latest' directory
-```
-
-This is normal and the site will work the same way.
-
-### Version Already Exists
-
-If you see this warning:
-```
-[WARN] Version 9.2.0 already exists
-```
-
-Options:
-1. Answer `y` to overwrite (useful for SNAPSHOT versions)
-2. Answer `n` and use a different version number
-3. Manually delete the old version first: `rm -rf
juneau-docs/static/javadocs/9.2.0`
-
-## Requirements
-
-- **Python**: 3.6 or higher
-- **Javadocs**: Pre-generated (run `mvn javadoc:javadoc` first)
-- **Disk Space**: Each version requires ~50-100 MB
-
-## Replacing the Old Script
-
-This Python script replaces `/juneau-docs/add-javadocs.sh` with:
-- ✅ Better validation and error messages
-- ✅ Clearer prompts and output
-- ✅ Cross-platform symlink support with fallback
-- ✅ More robust copy operations
-- ✅ Colored output for better UX
-
-## Best Practices
-
-1. **Use Semantic Versioning** - Follow the pattern: `MAJOR.MINOR.PATCH`
-2. **Always Use `--make-latest` for Current Release** - Keeps the "latest"
link updated
-3. **Don't Make Old Versions Latest** - Only the current release should be
"latest"
-4. **Test Before Committing** - Always view Javadocs in browser first
-5. **Commit Versioned Javadocs** - They're part of the documentation history
-
-## Notes
-
-- Javadocs are static files and safe to commit to Git
-- Each version is independent - no shared files between versions
-- The "latest" link is for convenience - users can also access by specific
version
-- Javadoc URLs in documentation should use version numbers, not "latest"
-- The script preserves timestamps and permissions from the source
-
diff --git a/scripts/README-build-and-push.md b/scripts/README-build-and-push.md
index 987cacacb9..dc57cc3665 100644
--- a/scripts/README-build-and-push.md
+++ b/scripts/README-build-and-push.md
@@ -2,7 +2,7 @@
## Overview
-`build-and-push.py` automates the complete build, test, and deployment
workflow for Juneau, ensuring code quality before pushing changes to the remote
repository.
+`push.py` automates the complete build, test, and deployment workflow for
Juneau, ensuring code quality before pushing changes to the remote repository.
## What It Does
@@ -22,31 +22,31 @@ If any step fails, the script stops immediately and reports
the failure.
```bash
cd /Users/james.bognar/git/juneau
-python3 scripts/build-and-push.py "Your commit message here"
+python3 scripts/push.py "Your commit message here"
```
### Skip Tests (for documentation-only changes)
```bash
-python3 scripts/build-and-push.py "Updated README" --skip-tests
+python3 scripts/push.py "Updated README" --skip-tests
```
### Skip Javadoc Generation
```bash
-python3 scripts/build-and-push.py "Minor code fix" --skip-javadoc
+python3 scripts/push.py "Minor code fix" --skip-javadoc
```
### Skip Both Tests and Javadoc
```bash
-python3 scripts/build-and-push.py "Quick formatting fix" --skip-tests
--skip-javadoc
+python3 scripts/push.py "Quick formatting fix" --skip-tests --skip-javadoc
```
### Dry Run (see what would happen without actually doing it)
```bash
-python3 scripts/build-and-push.py "Testing changes" --dry-run
+python3 scripts/push.py "Testing changes" --dry-run
```
## Command-Line Options
@@ -66,7 +66,7 @@ python3 scripts/build-and-push.py "Testing changes" --dry-run
### Example 1: Full Build and Push
```bash
-python3 scripts/build-and-push.py "Fixed bug in RestClient connection handling"
+python3 scripts/push.py "Fixed bug in RestClient connection handling"
```
Output:
@@ -112,19 +112,19 @@ Running: git push
### Example 2: Documentation Changes (Skip Tests)
```bash
-python3 scripts/build-and-push.py "Updated REST client documentation"
--skip-tests
+python3 scripts/push.py "Updated REST client documentation" --skip-tests
```
### Example 3: Formatting Changes (Skip Tests and Javadoc)
```bash
-python3 scripts/build-and-push.py "Fixed code formatting in BeanContext"
--skip-tests --skip-javadoc
+python3 scripts/push.py "Fixed code formatting in BeanContext" --skip-tests
--skip-javadoc
```
### Example 4: Dry Run
```bash
-python3 scripts/build-and-push.py "Testing my changes" --dry-run
+python3 scripts/push.py "Testing my changes" --dry-run
```
Output:
diff --git a/scripts/README-build-docs.md b/scripts/README-build-docs.md
index 9963f3582e..99b4b706a2 100644
--- a/scripts/README-build-docs.md
+++ b/scripts/README-build-docs.md
@@ -9,13 +9,17 @@
The script performs the following steps:
1. **Installs npm dependencies** - Runs `npm ci` in the `juneau-docs` directory
-2. **Builds Docusaurus** - Runs `npm run build` to generate the static
Docusaurus site
-3. **Compiles Java modules** - Runs `mvn clean compile` to ensure all source
files are available
-4. **Generates Maven site** - Runs `mvn site` to generate the Maven project
site
-5. **Generates aggregate javadocs** - Runs `mvn javadoc:aggregate` to create
aggregated API documentation
-6. **Copies Maven site** - Copies the generated site to
`juneau-docs/build/site/`
-7. **Copies .asf.yaml** - Copies the ASF configuration file to the build
directory
-8. **Verifies apidocs** - Checks that the javadocs were successfully generated
+2. **Compiles Java modules** - Runs `mvn clean install -DskipTests` to build
all modules
+3. **Generates Maven site** - Runs `mvn site -DskipTests` to generate the
Maven project site (includes aggregate javadocs)
+4. **Copies current javadocs** - Copies generated javadocs to
`juneau-docs/javadocs/<current-release>/`
+5. **Updates javadocs index** - Updates `releases.json` with version
information
+6. **Creates build directory** - Creates `juneau-docs/build/` directory
+7. **Copies Maven site** - Copies the generated site to
`juneau-docs/build/site/`
+8. **Copies javadocs** - Copies javadocs to `juneau-docs/build/javadocs/`
+9. **Builds Docusaurus** - Runs `npm run build` to generate the static
Docusaurus site
+10. **Copies .asf.yaml** - Copies the ASF configuration file to the build
directory
+11. **Verifies apidocs** - Checks that the javadocs were successfully generated
+12. **Checks topic links** - Validates all documentation links
## Usage
@@ -42,10 +46,20 @@ python3 scripts/build-docs.py --skip-maven
# Skip copying step (useful for testing without updating build directory)
python3 scripts/build-docs.py --skip-copy
+# Build for staging (sets SITE_URL for staging environment)
+python3 scripts/build-docs.py --staging
+
# Combine options
python3 scripts/build-docs.py --skip-npm --skip-copy
```
+## Command-Line Options
+
+- `--skip-npm` - Skip npm install and Docusaurus build
+- `--skip-maven` - Skip Maven compilation and site generation
+- `--skip-copy` - Skip copying Maven site to build directory
+- `--staging` - Build for staging (sets SITE_URL to juneau.staged.apache.org)
+
## Prerequisites
The script requires the following tools to be installed and available in your
PATH:
@@ -63,7 +77,8 @@ After successful execution, the documentation will be
available in:
- **Docusaurus docs**: `juneau-docs/build/`
- **Maven site**: `juneau-docs/build/site/`
-- **Javadocs**: `juneau-docs/build/site/apidocs/`
+- **Javadocs**: `juneau-docs/build/javadocs/` (versioned) and
`juneau-docs/build/site/apidocs/` (current)
+- **Javadocs index**: `juneau-docs/build/javadocs/index.html` (dynamically
loads from `releases.json`)
The script will also verify that the javadocs were generated and report the
number of HTML files found.
@@ -88,8 +103,7 @@ Running: mvn clean compile -DskipTests
=== Generating Maven site ===
Running: mvn site -DskipTests
-=== Generating aggregate javadocs ===
-Running: mvn javadoc:aggregate -DskipTests
+=== Copying current javadocs to versioned folder ===
=== Verifying apidocs generation ===
✓ Javadocs found in /path/to/juneau/target/site/apidocs
@@ -202,15 +216,18 @@ If the script reports that apidocs were not found:
- The build directory (`juneau-docs/build/`) will be created if it doesn't
exist
- Existing site contents will be replaced when copying
+## Build Order
+
+The script now copies Maven site and javadocs **before** building Docusaurus.
This prevents Docusaurus from showing broken links during startup, as the
static files are already in place.
+
## Integration with CI/CD
-This script is used by the GitHub Actions workflow
(`.github/workflows/deploy-docs.yml`) to build documentation. The workflow runs:
+This script is used by the documentation release scripts:
+- `release-docs-stage.py` - Calls `build-docs.py --staging` to build for
staging
+- `release-docs.py` - Promotes already-built documentation from staging to
production
-```yaml
-- name: Build Documentation
- run: |
- python3 scripts/build-docs.py
-```
+## Related Scripts
-This ensures that the same build process is used both locally and in CI/CD.
+- `release-docs-stage.py` - Deploys built documentation to `asf-staging` branch
+- `release-docs.py` - Promotes documentation from `asf-staging` to `asf-site`
branch
diff --git a/scripts/README-current-release.md
b/scripts/README-current-release.md
new file mode 100644
index 0000000000..6f584e9abe
--- /dev/null
+++ b/scripts/README-current-release.md
@@ -0,0 +1,55 @@
+# Current Release Version Script
+
+## Overview
+
+`current-release.py` extracts the current release version from the root
`pom.xml` file, removing the `-SNAPSHOT` suffix if present.
+
+## What It Does
+
+The script:
+1. Tries to use Maven's `help:evaluate` to get the version (most reliable)
+2. Falls back to parsing the `pom.xml` XML directly if Maven fails
+3. Removes the `-SNAPSHOT` suffix if present
+4. Prints the version number to stdout
+
+## Usage
+
+```bash
+python3 scripts/current-release.py
+```
+
+## Output
+
+Prints the version number (e.g., "9.2.0") to stdout.
+
+### Example
+
+```bash
+$ python3 scripts/current-release.py
+9.2.0
+```
+
+## Exit Codes
+
+- `0` - Success, version printed to stdout
+- `1` - Error, could not determine version
+
+## Use Cases
+
+This script is typically used by other scripts (like `release.py`) to:
+- Determine the current release version
+- Calculate the next version
+- Generate version-specific file names
+
+## Requirements
+
+- Python 3.6 or higher
+- Maven (optional, for primary method)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- The script prefers Maven's evaluation method as it's more reliable
+- Falls back to XML parsing if Maven is not available or fails
+- Handles Maven POM namespaces correctly
+
diff --git a/scripts/README-maven-version.md b/scripts/README-maven-version.md
new file mode 100644
index 0000000000..4e91efc05f
--- /dev/null
+++ b/scripts/README-maven-version.md
@@ -0,0 +1,53 @@
+# Maven Version Script
+
+## Overview
+
+`maven-version.py` extracts the Maven major version number from the output of
`mvn -version`.
+
+## What It Does
+
+The script:
+1. Runs `mvn -version`
+2. Parses the output to extract the major version number
+3. Prints the major version (e.g., "3") to stdout
+
+## Usage
+
+```bash
+python3 scripts/maven-version.py
+```
+
+## Output
+
+Prints the major version number (e.g., "3") to stdout.
+
+### Example
+
+```bash
+$ python3 scripts/maven-version.py
+3
+```
+
+## Exit Codes
+
+- `0` - Success, version printed to stdout
+- `1` - Error, could not determine Maven version or Maven not found
+
+## Use Cases
+
+This script is typically used by other scripts (like `release.py`) to:
+- Check if Maven meets minimum version requirements
+- Verify Maven installation
+
+## Requirements
+
+- Python 3.6 or higher
+- Maven (mvn command must be in PATH)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- The script looks for patterns like "Apache Maven 3.9.5" or "Maven 3.9.5"
+- Returns only the major version number (e.g., "3" from "3.9.5")
+- Exits with error if Maven is not found or version cannot be parsed
+
diff --git a/scripts/README-prompt-pgp-passphrase.md
b/scripts/README-prompt-pgp-passphrase.md
new file mode 100644
index 0000000000..798bc67a77
--- /dev/null
+++ b/scripts/README-prompt-pgp-passphrase.md
@@ -0,0 +1,45 @@
+# PGP Passphrase Prompt Script
+
+## Overview
+
+`prompt-pgp-passphrase.py` makes a dummy PGP call to prompt for the user's
passphrase early in the execution. This ensures the user is prompted at the
beginning rather than waiting until signing is needed near the end of a process.
+
+## What It Does
+
+The script:
+1. Creates a temporary dummy file
+2. Attempts to sign it with GPG (which prompts for passphrase)
+3. Cleans up the temporary files
+4. Returns success even if GPG is not available (to avoid breaking workflows)
+
+## Usage
+
+```bash
+python3 scripts/prompt-pgp-passphrase.py
+```
+
+## When to Use
+
+This script is typically called by other scripts (like `push.py` and
`release.py`) at the beginning of their execution to:
+- Prompt for PGP passphrase early
+- Avoid delays later when signing is actually needed
+- Provide a better user experience
+
+## Exit Codes
+
+- Always returns `0` (success) to avoid breaking workflows
+- Prints warnings if GPG is not available or if the prompt times out
+
+## Requirements
+
+- Python 3.6 or higher
+- GPG (optional, script handles gracefully if not available)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- The script is designed to fail gracefully
+- If GPG is not found, it prints a warning and continues
+- If the prompt times out, it's treated as okay (user may not need signing)
+- The dummy file and signature are automatically cleaned up
+
diff --git a/scripts/README-release-docs-stage.md
b/scripts/README-release-docs-stage.md
new file mode 100644
index 0000000000..49a95c29f9
--- /dev/null
+++ b/scripts/README-release-docs-stage.md
@@ -0,0 +1,71 @@
+# Release Documentation to Staging Script
+
+## Overview
+
+`release-docs-stage.py` builds the documentation and deploys it to the
`asf-staging` branch, making it available on the staging website.
+
+## What It Does
+
+The script:
+1. Runs `build-docs.py` with `--staging` flag to build documentation for
staging
+2. Clones the repository to a temporary directory
+3. Checks out the `asf-staging` branch (or creates it if it doesn't exist)
+4. Removes all existing files (except `.git`)
+5. Copies the built documentation from `juneau-docs/build` to the temp
directory
+6. Adds and commits the changes
+7. Pushes to the remote `asf-staging` branch
+
+## Usage
+
+### Deploy to Staging
+
+```bash
+python3 scripts/release-docs-stage.py
+```
+
+### Custom Commit Message
+
+```bash
+python3 scripts/release-docs-stage.py --commit-message "Updated API
documentation"
+```
+
+### Test Without Pushing
+
+```bash
+python3 scripts/release-docs-stage.py --no-push
+```
+
+## Command-Line Options
+
+- `--no-push` - Build and commit but don't push to remote
+- `--commit-message MESSAGE` - Custom commit message (default: "Deploy
documentation staging")
+
+## Prerequisites
+
+- Python 3.6 or higher
+- Git (git command must be in PATH)
+- All prerequisites for `build-docs.py` (Node.js, npm, Maven, Java)
+- Write access to the remote repository
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Error occurred
+
+## Notes
+
+- The script uses a temporary directory for git operations
+- The temp directory is automatically cleaned up after successful push
+- If `--no-push` is used, the temp directory is not cleaned up (for manual
inspection)
+- The script sets `SITE_URL` environment variable to
`https://juneau.staged.apache.org` when building
+- A success sound is played when the script completes successfully
+
+## Workflow
+
+This script is typically used as part of the documentation release workflow:
+
+1. Make documentation changes
+2. Run `release-docs-stage.py` to deploy to staging
+3. Review the staging site
+4. Run `release-docs.py` to promote to production
+
diff --git a/scripts/README-release-docs.md b/scripts/README-release-docs.md
new file mode 100644
index 0000000000..4168c162ea
--- /dev/null
+++ b/scripts/README-release-docs.md
@@ -0,0 +1,67 @@
+# Promote Documentation to Production Script
+
+## Overview
+
+`release-docs.py` promotes documentation from the `asf-staging` branch to the
`asf-site` branch, making it live on the production website.
+
+## What It Does
+
+The script:
+1. Clones the repository to a temporary directory
+2. Fetches the `asf-staging` branch
+3. Switches to a detached HEAD at `origin/asf-staging`
+4. Force pushes to the `asf-site` branch
+
+## Usage
+
+### Promote to Production
+
+```bash
+python3 scripts/release-docs.py
+```
+
+**⚠️ WARNING:** This will make the documentation live on the production
website.
+
+### Test Without Pushing
+
+```bash
+python3 scripts/release-docs.py --no-push
+```
+
+## Command-Line Options
+
+- `--no-push` - Perform all steps except the final git push
+- `--commit-message` - Not used (kept for consistency with
release-docs-stage.py)
+
+## Prerequisites
+
+- Python 3.6 or higher
+- Git (git command must be in PATH)
+- Write access to the remote repository
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Error occurred
+
+## Notes
+
+- The script uses a temporary directory for git operations
+- The temp directory is automatically cleaned up after successful push
+- If `--no-push` is used, the temp directory is not cleaned up (for manual
inspection)
+- A success sound is played when the script completes successfully
+- This script does NOT build documentation - it only promotes what's already
in `asf-staging`
+
+## Workflow
+
+This script is typically used as the final step in the documentation release
workflow:
+
+1. Make documentation changes
+2. Run `release-docs-stage.py` to deploy to staging
+3. Review the staging site at `https://juneau.staged.apache.org`
+4. Run `release-docs.py` to promote to production at
`https://juneau.apache.org`
+
+## Safety
+
+The script includes a warning message before promoting to production to ensure
you understand the impact.
+
diff --git a/scripts/README-release.md b/scripts/README-release.md
new file mode 100644
index 0000000000..bdd00604fc
--- /dev/null
+++ b/scripts/README-release.md
@@ -0,0 +1,134 @@
+# Release Script
+
+## Overview
+
+`release.py` automates the complete release process for Apache Juneau,
including prerequisite checks, building, testing, Maven release, binary
artifact creation, and SVN distribution upload.
+
+## What It Does
+
+The script performs the following steps:
+
+1. **Check Prerequisites** - Verifies required tools are available
+2. **Check Java Version** - Automatically detects and verifies Java 17+
+3. **Check Maven Version** - Automatically detects and verifies Maven 3+
+4. **Clean Maven Repository** - Cleans local Maven repo (only on first run for
a version)
+5. **Make Git Folder** - Creates staging directory
+6. **Clone Juneau** - Clones the repository to staging directory
+7. **Configure Git** - Sets up git user and email
+8. **Run Clean Verify** - Builds and verifies the project
+9. **Run Javadoc Aggregate** - Generates aggregate javadocs
+10. **Create Test Workspace** - Creates a test workspace
+11. **Run Deploy** - Deploys to Maven staging repository
+12. **Run Release Prepare** - Prepares the Maven release
+13. **Run Git Diff** - Shows changes made by release:prepare
+14. **Run Release Perform** - Performs the Maven release
+15. **Create Binary Artifacts** - Downloads and processes source/binary
artifacts
+16. **Verify Distribution** - Verifies files on Apache distribution site
+
+## Usage
+
+### Start a New Release
+
+```bash
+python3 scripts/release.py --rc 1
+```
+
+### Resume from a Step
+
+```bash
+python3 scripts/release.py --start-step run_release_perform
+```
+
+### List Available Steps
+
+```bash
+python3 scripts/release.py --list-steps
+```
+
+### Skip Specific Steps
+
+```bash
+python3 scripts/release.py --rc 1 --skip-step clean_maven_repo
+```
+
+### Resume from Last Checkpoint
+
+```bash
+python3 scripts/release.py --resume
+```
+
+## Command-Line Options
+
+### Required (for new releases)
+- `--rc RC_NUMBER` - Release candidate number (e.g., `--rc 1` for RC1)
+
+### Optional
+- `--start-step STEP_NAME` - Start execution from the specified step
+- `--list-steps` - List all available steps and exit
+- `--skip-step STEP_NAME` - Skip a specific step (can be used multiple times)
+- `--resume` - Resume from the last checkpoint (if available)
+
+## Environment Variables
+
+The script prompts for and sets the following environment variables:
+
+- `X_VERSION` - Current release version (detected from pom.xml)
+- `X_NEXT_VERSION` - Next version (calculated)
+- `X_RELEASE` - Full release name (e.g., "juneau-9.2.0-RC1")
+- `X_RELEASE_CANDIDATE` - Release candidate number
+- `X_STAGING` - Staging directory path
+- `X_USERNAME` - Apache username
+- `X_EMAIL` - Apache email
+- `X_GIT_BRANCH` - Git branch name
+- `X_JAVA_HOME` - Java home directory
+- `X_CLEANM2` - Whether to clean Maven repo (Y/N)
+
+## History Files
+
+The script maintains history files (`release-history-{version}.json`) that
store:
+- Default values for prompts
+- Last run date
+- Previous configuration values
+
+This allows the script to provide intelligent defaults on subsequent runs.
+
+## Checkpoint/Resume
+
+The script supports checkpoint/resume functionality:
+- State is saved to `~/.juneau-release-state.json`
+- You can resume from any step using `--resume` or `--start-step`
+- The `--rc` parameter is optional when resuming (extracted from state/history)
+
+## Vote Email Generation
+
+After successful distribution verification, the script automatically generates
a vote email body with:
+- Version and RC number
+- Distribution URLs
+- SHA-512 checksums (fetched from URLs)
+- Git commit hash
+- Vote end date (72 hours from now)
+
+The email is printed to the console.
+
+## Prerequisites
+
+- Python 3.6 or higher
+- Java 17 or higher
+- Maven 3 or higher
+- Git
+- GPG (for signing)
+- SVN client (for distribution upload)
+- wget (for downloading artifacts)
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Error occurred
+
+## Notes
+
+- The script automatically excludes `juneau-docs` from the source release zip
+- Source and binary artifacts are downloaded from Maven staging repository
+- Distribution verification checks for expected files on Apache distribution
site
+- The script uses `current-release.py` and `maven-version.py` for version
detection
+
diff --git a/scripts/README-revert-staged.md b/scripts/README-revert-staged.md
new file mode 100644
index 0000000000..7091ed299c
--- /dev/null
+++ b/scripts/README-revert-staged.md
@@ -0,0 +1,46 @@
+# Revert Staged Changes Script
+
+## Overview
+
+`revert-staged.py` reverts both staged AND unstaged changes for a file back to
the last committed version (HEAD).
+
+## What It Does
+
+The script:
+1. Verifies the file is tracked by git
+2. Warns the user about discarding changes
+3. Runs `git restore --source=HEAD <file>` to revert the file
+
+## Usage
+
+```bash
+python3 scripts/revert-staged.py <file_path>
+```
+
+### Example
+
+```bash
+python3 scripts/revert-staged.py
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+```
+
+## ⚠️ Warning
+
+This will discard **ALL changes** (both staged and unstaged) for the specified
file, reverting it back to the last commit.
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Error (file not tracked, git command failed, etc.)
+
+## Requirements
+
+- Python 3.6 or higher
+- Git (git command must be in PATH)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- The file must be tracked by git (the script verifies this)
+- This is a destructive operation - changes cannot be recovered
+- Use `revert-unstaged.py` if you only want to revert unstaged changes while
preserving staged changes
+
diff --git a/scripts/README-revert-unstaged.md
b/scripts/README-revert-unstaged.md
new file mode 100644
index 0000000000..a7df4f183b
--- /dev/null
+++ b/scripts/README-revert-unstaged.md
@@ -0,0 +1,50 @@
+# Revert Unstaged Changes Script
+
+## Overview
+
+`revert-unstaged.py` reverts working directory changes back to what's in the
staging area (INDEX), preserving any staged changes.
+
+## What It Does
+
+The script:
+1. Verifies the file exists
+2. Runs `git restore --source=INDEX <file>` to revert unstaged changes
+3. Preserves any staged changes
+
+## Usage
+
+```bash
+python3 scripts/revert-unstaged.py <file_path>
+```
+
+### Example
+
+```bash
+python3 scripts/revert-unstaged.py
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+```
+
+## Use Case
+
+This is useful when you have:
+- Staged changes that you've tested and want to keep
+- Unstaged changes that you want to discard
+
+The script will revert the working directory to match the staged version,
effectively discarding only the unstaged changes.
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Error (file not found, git command failed, etc.)
+
+## Requirements
+
+- Python 3.6 or higher
+- Git (git command must be in PATH)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- This only affects unstaged changes - staged changes are preserved
+- Use `revert-staged.py` if you want to revert both staged and unstaged changes
+- The file must exist (the script verifies this)
+
diff --git a/scripts/README-test.md b/scripts/README-test.md
new file mode 100644
index 0000000000..aa44a58208
--- /dev/null
+++ b/scripts/README-test.md
@@ -0,0 +1,108 @@
+# Test Script
+
+## Overview
+
+`test.py` is a build and test helper script for Apache Juneau. It provides a
convenient way to run Maven builds and tests with various options.
+
+## What It Does
+
+The script can:
+- Build the project (clean install without tests)
+- Run tests
+- Do both (full clean build + tests)
+
+## Usage
+
+### Full Build and Test (Default)
+
+```bash
+python3 scripts/test.py
+# or
+python3 scripts/test.py --full
+# or
+python3 scripts/test.py -f
+```
+
+This performs a clean build and runs all tests.
+
+### Build Only (Skip Tests)
+
+```bash
+python3 scripts/test.py --build-only
+# or
+python3 scripts/test.py -b
+```
+
+This runs `mvn clean install -DskipTests` to build the project without running
tests.
+
+### Test Only (No Build)
+
+```bash
+python3 scripts/test.py --test-only
+# or
+python3 scripts/test.py -t
+```
+
+This runs `mvn test -Drat.skip=true` to run tests without rebuilding.
+
+### Verbose Output
+
+```bash
+python3 scripts/test.py --verbose
+# or
+python3 scripts/test.py -v
+```
+
+Shows full Maven output instead of just the last 50 lines.
+
+## Command-Line Options
+
+- `--build-only, -b` - Only build (skip tests)
+- `--test-only, -t` - Only run tests (no build)
+- `--full, -f` - Clean build + run tests (default)
+- `--verbose, -v` - Show full Maven output
+- `--help, -h` - Show help message
+
+## Examples
+
+### Quick Test Run
+
+```bash
+python3 scripts/test.py -t
+```
+
+### Rebuild After Code Changes
+
+```bash
+python3 scripts/test.py -b
+```
+
+### Full Build and Test with Verbose Output
+
+```bash
+python3 scripts/test.py -f -v
+```
+
+## Output
+
+By default, the script shows the last 50 lines of Maven output. Use
`--verbose` to see the full output.
+
+The script also attempts to parse test results and display failure/error
counts if tests fail.
+
+## Exit Codes
+
+- `0` - Success
+- `1` - Failure (build or tests failed)
+
+## Requirements
+
+- Python 3.6 or higher
+- Maven (mvn command must be in PATH)
+- No external Python dependencies (uses only standard library)
+
+## Notes
+
+- The script automatically skips RAT checks when running tests
(`-Drat.skip=true`)
+- Tests are skipped during build (`-DskipTests`)
+- All commands are run from the project root directory
+
diff --git a/scripts/add-versioned-javadocs.py
b/scripts/add-versioned-javadocs.py
deleted file mode 100755
index 109761f5b1..0000000000
--- a/scripts/add-versioned-javadocs.py
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/env python3
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Add Versioned Javadocs Script
-
-Copies generated Javadocs to the static documentation site with version
control.
-
-Usage:
- python3 add-versioned-javadocs.py <version> <source_path> [--make-latest]
-
-Examples:
- python3 add-versioned-javadocs.py 9.2.0 ../target/site/apidocs
- python3 add-versioned-javadocs.py 9.1.0 /path/to/old/javadocs --make-latest
-"""
-
-import argparse
-import os
-import shutil
-import sys
-from pathlib import Path
-
-
-class Colors:
- """ANSI color codes for terminal output."""
- GREEN = '\033[0;32m'
- YELLOW = '\033[1;33m'
- RED = '\033[0;31m'
- NC = '\033[0m' # No Color
-
-
-def print_info(message):
- """Print info message in green."""
- print(f"{Colors.GREEN}[INFO]{Colors.NC} {message}")
-
-
-def print_warn(message):
- """Print warning message in yellow."""
- print(f"{Colors.YELLOW}[WARN]{Colors.NC} {message}")
-
-
-def print_error(message):
- """Print error message in red."""
- print(f"{Colors.RED}[ERROR]{Colors.NC} {message}")
-
-
-def validate_source(source_path):
- """Validate that the source path contains valid javadocs."""
- source = Path(source_path)
-
- if not source.exists():
- print_error(f"Source path does not exist: {source_path}")
- return False
-
- if not source.is_dir():
- print_error(f"Source path is not a directory: {source_path}")
- return False
-
- index_file = source / "index.html"
- if not index_file.exists():
- print_error(f"Source path does not appear to contain javadocs (missing
index.html)")
- return False
-
- return True
-
-
-def prompt_overwrite():
- """Prompt user to confirm overwrite of existing version."""
- try:
- response = input("Do you want to overwrite it? (y/N) ").strip().lower()
- return response in ['y', 'yes']
- except (EOFError, KeyboardInterrupt):
- print()
- return False
-
-
-def copy_javadocs(source_path, target_dir):
- """Copy javadocs from source to target directory."""
- source = Path(source_path)
- target = Path(target_dir)
-
- # Create target directory
- target.mkdir(parents=True, exist_ok=True)
-
- # Copy all files
- for item in source.iterdir():
- dest = target / item.name
- if item.is_dir():
- if dest.exists():
- shutil.rmtree(dest)
- shutil.copytree(item, dest)
- else:
- shutil.copy2(item, dest)
-
-
-def update_latest_link(javadocs_dir, version):
- """Update the 'latest' link to point to the specified version."""
- javadocs_path = Path(javadocs_dir)
- latest_path = javadocs_path / "latest"
- version_path = javadocs_path / version
-
- # Remove existing latest (whether symlink or directory)
- if latest_path.exists() or latest_path.is_symlink():
- if latest_path.is_dir() and not latest_path.is_symlink():
- shutil.rmtree(latest_path)
- else:
- latest_path.unlink()
-
- # Try to create symlink
- try:
- latest_path.symlink_to(version)
- print_info(f"Created symlink: latest -> {version}")
- except (OSError, NotImplementedError):
- # Symlinks not supported, copy instead
- print_warn("Symlinks not supported, copying instead...")
- shutil.copytree(version_path, latest_path)
- print_info("Copied to 'latest' directory")
-
-
-def main():
- parser = argparse.ArgumentParser(
- description="Add versioned javadocs to the static documentation site",
- formatter_class=argparse.RawDescriptionHelpFormatter,
- epilog="""
-Examples:
- python3 add-versioned-javadocs.py 9.2.0 ../target/site/apidocs
- python3 add-versioned-javadocs.py 9.1.0 /path/to/old/javadocs --make-latest
- """
- )
-
- parser.add_argument(
- "version",
- help="Version number (e.g., 9.2.0)"
- )
-
- parser.add_argument(
- "source_path",
- help="Path to the javadocs to copy (e.g., ../target/site/apidocs)"
- )
-
- parser.add_argument(
- "--make-latest",
- action="store_true",
- help="Update the 'latest' symlink to this version"
- )
-
- args = parser.parse_args()
-
- # Get directories
- script_dir = Path(__file__).parent
- juneau_root = script_dir.parent
- javadocs_dir = juneau_root / "juneau-docs" / "static" / "javadocs"
- target_dir = javadocs_dir / args.version
-
- # Validate source
- if not validate_source(args.source_path):
- return 1
-
- # Check if version already exists
- if target_dir.exists():
- print_warn(f"Version {args.version} already exists at {target_dir}")
- if not prompt_overwrite():
- print_info("Aborted.")
- return 0
- print_info("Removing existing version...")
- shutil.rmtree(target_dir)
-
- # Copy javadocs
- print_info(f"Creating directory for version {args.version}...")
- print_info(f"Copying javadocs from {args.source_path} to {target_dir}...")
-
- try:
- copy_javadocs(args.source_path, target_dir)
- except Exception as e:
- print_error(f"Copy failed: {e}")
- return 1
-
- # Verify copy
- if not (target_dir / "index.html").exists():
- print_error("Copy failed - index.html not found in target directory")
- return 1
-
- print_info(f"Successfully added javadocs for version {args.version}")
-
- # Update latest if requested
- if args.make_latest:
- print_info(f"Updating 'latest' to point to version {args.version}...")
- update_latest_link(javadocs_dir, args.version)
-
- # Print summary
- print()
- print_info("=" * 48)
- print_info(f"Javadocs for version {args.version} successfully added!")
- print_info("=" * 48)
- print()
- print(f" Location: {target_dir}")
- print(f" URL (after deployment): /javadocs/{args.version}/")
- if args.make_latest:
- print(" Latest URL: /javadocs/latest/")
- print()
- print_info("Next steps:")
- print(f" 1. Review the copied files: ls -lh {target_dir}")
- print(" 2. Test locally: python3 scripts/start-docusaurus.py")
- print(" 3. Visit http://localhost:3000/javadocs/")
- print(" 4. Commit the changes if everything looks good")
- print(" 5. Consider updating index.html to add this version to the
version grid")
- print()
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main())
-
diff --git a/scripts/build-docs.py b/scripts/build-docs.py
index 699cbdab03..0ee04c65dc 100755
--- a/scripts/build-docs.py
+++ b/scripts/build-docs.py
@@ -312,34 +312,36 @@ def main():
else:
print("\n=== Skipping Maven steps ===")
- # Build Docusaurus documentation first (creates build directory)
- if not args.skip_npm:
- build_docusaurus(docs_dir, staging=args.staging)
- else:
- print("\n=== Skipping Docusaurus build ===")
-
- # Copy Maven site directly to build directory (after Docusaurus
creates it)
+ # Create build directory and copy Maven site/javadocs BEFORE building
Docusaurus
+ # This prevents Docusaurus from showing broken links during startup
if not args.skip_copy:
+ build_dir.mkdir(parents=True, exist_ok=True)
+
+ # Copy Maven site to build directory
copy_maven_site(project_root, docs_dir)
- else:
- print("\n=== Skipping copy step ===")
-
- # Copy javadocs to build directory (after Docusaurus build)
- if not args.skip_copy:
+
+ # Copy javadocs to build directory
source_javadocs = Path(docs_dir) / 'javadocs'
- build_dir = Path(docs_dir) / 'build'
build_javadocs = build_dir / 'javadocs'
- if source_javadocs.exists() and build_dir.exists():
+ if source_javadocs.exists():
print(f"\n=== Copying javadocs to build directory ===")
print(f"Copying {source_javadocs} to {build_javadocs}")
if build_javadocs.exists():
shutil.rmtree(build_javadocs)
shutil.copytree(source_javadocs, build_javadocs)
print(f"✓ Javadocs copied successfully")
- elif not source_javadocs.exists():
+ else:
print(f"\n=== WARNING: Javadocs directory not found at
{source_javadocs} ===")
print("Skipping javadocs copy step")
+ else:
+ print("\n=== Skipping copy step ===")
+
+ # Build Docusaurus documentation (build directory already exists with
site/javadocs)
+ if not args.skip_npm:
+ build_docusaurus(docs_dir, staging=args.staging)
+ else:
+ print("\n=== Skipping Docusaurus build ===")
# Copy .asf.yaml to build directory (needed for deployment)
if not args.skip_copy:
diff --git a/scripts/release-docs-stage.py b/scripts/release-docs-stage.py
index 967bb2735e..dcb4742db0 100755
--- a/scripts/release-docs-stage.py
+++ b/scripts/release-docs-stage.py
@@ -31,6 +31,7 @@ Options:
import argparse
import os
+import platform
import shutil
import subprocess
import sys
@@ -80,6 +81,72 @@ def get_git_remote_url():
return None
+def play_sound(success=True):
+ """
+ Play a system sound to indicate success or failure.
+
+ Args:
+ success: True for success sound, False for failure sound
+ """
+ try:
+ system = platform.system()
+ if system == "Darwin": # macOS
+ if success:
+ # Success sound
+ sound_path = "/System/Library/Sounds/Glass.aiff"
+ else:
+ # Failure sound
+ sound_path = "/System/Library/Sounds/Basso.aiff"
+
+ if os.path.exists(sound_path):
+ subprocess.run(
+ ["afplay", sound_path],
+ capture_output=True,
+ timeout=5
+ )
+ elif system == "Linux":
+ # Try to use paplay (PulseAudio) or aplay (ALSA)
+ if success:
+ # Try to play a beep or use speaker-test
+ try:
+ subprocess.run(
+ ["paplay",
"/usr/share/sounds/freedesktop/stereo/complete.oga"],
+ capture_output=True,
+ timeout=5
+ )
+ except:
+ # Fallback to speaker-test
+ subprocess.run(
+ ["speaker-test", "-t", "sine", "-f", "1000", "-l",
"1"],
+ capture_output=True,
+ timeout=2
+ )
+ else:
+ try:
+ subprocess.run(
+ ["paplay",
"/usr/share/sounds/freedesktop/stereo/dialog-error.oga"],
+ capture_output=True,
+ timeout=5
+ )
+ except:
+ # Fallback to speaker-test with lower frequency
+ subprocess.run(
+ ["speaker-test", "-t", "sine", "-f", "400", "-l", "1"],
+ capture_output=True,
+ timeout=2
+ )
+ elif system == "Windows":
+ # Use winsound module
+ import winsound
+ if success:
+ winsound.MessageBeep(winsound.MB_OK)
+ else:
+ winsound.MessageBeep(winsound.MB_ICONHAND)
+ except Exception:
+ # Silently fail if sound can't be played
+ pass
+
+
def main():
parser = argparse.ArgumentParser(
description="Release documentation to asf-staging branch",
@@ -286,6 +353,9 @@ def main():
print(f"\nTemporary directory: {temp_dir}")
print("(This directory will not be automatically cleaned up)")
+ # Play success sound
+ play_sound(success=True)
+
except KeyboardInterrupt:
print("\n\n⚠️ Process interrupted by user")
sys.exit(1)
diff --git a/scripts/release-docs-stage.py b/scripts/release-docs.py
similarity index 55%
copy from scripts/release-docs-stage.py
copy to scripts/release-docs.py
index 967bb2735e..dfa824b992 100755
--- a/scripts/release-docs-stage.py
+++ b/scripts/release-docs.py
@@ -12,25 +12,27 @@
# * specific language governing permissions and limitations under the License.
*
#
***************************************************************************************************************************
"""
-Release Documentation to Staging Branch
+Promote Documentation from Staging to Production
-This script:
-1. Runs build-docs.py to build the documentation
-2. Checks out the asf-staging branch to a temporary directory
-3. Copies the contents of juneau-docs/build to the temp directory
-4. Adds and commits the changes
-5. Pushes to the remote asf-staging branch
+This script promotes the documentation from the asf-staging branch to the
asf-site branch.
+This makes the documentation live on the production website.
+
+The script:
+1. Fetches the asf-staging branch
+2. Switches to a detached HEAD at origin/asf-staging
+3. Force pushes to the asf-site branch
Usage:
- python3 scripts/release-docs-stage.py [--no-push] [--commit-message
MESSAGE]
+ python3 scripts/release-docs.py [--no-push] [--commit-message MESSAGE]
Options:
- --no-push Build and commit but don't push to remote
- --commit-message Custom commit message (default: "Deploy documentation
staging")
+ --no-push Perform all steps except the final git push
+ --commit-message Not used (kept for consistency with
release-docs-stage.py)
"""
import argparse
import os
+import platform
import shutil
import subprocess
import sys
@@ -80,58 +82,101 @@ def get_git_remote_url():
return None
+def play_sound(success=True):
+ """
+ Play a system sound to indicate success or failure.
+
+ Args:
+ success: True for success sound, False for failure sound
+ """
+ try:
+ system = platform.system()
+ if system == "Darwin": # macOS
+ if success:
+ # Success sound
+ sound_path = "/System/Library/Sounds/Glass.aiff"
+ else:
+ # Failure sound
+ sound_path = "/System/Library/Sounds/Basso.aiff"
+
+ if os.path.exists(sound_path):
+ subprocess.run(
+ ["afplay", sound_path],
+ capture_output=True,
+ timeout=5
+ )
+ elif system == "Linux":
+ # Try to use paplay (PulseAudio) or aplay (ALSA)
+ if success:
+ # Try to play a beep or use speaker-test
+ try:
+ subprocess.run(
+ ["paplay",
"/usr/share/sounds/freedesktop/stereo/complete.oga"],
+ capture_output=True,
+ timeout=5
+ )
+ except:
+ # Fallback to speaker-test
+ subprocess.run(
+ ["speaker-test", "-t", "sine", "-f", "1000", "-l",
"1"],
+ capture_output=True,
+ timeout=2
+ )
+ else:
+ try:
+ subprocess.run(
+ ["paplay",
"/usr/share/sounds/freedesktop/stereo/dialog-error.oga"],
+ capture_output=True,
+ timeout=5
+ )
+ except:
+ # Fallback to speaker-test with lower frequency
+ subprocess.run(
+ ["speaker-test", "-t", "sine", "-f", "400", "-l", "1"],
+ capture_output=True,
+ timeout=2
+ )
+ elif system == "Windows":
+ # Use winsound module
+ import winsound
+ if success:
+ winsound.MessageBeep(winsound.MB_OK)
+ else:
+ winsound.MessageBeep(winsound.MB_ICONHAND)
+ except Exception:
+ # Silently fail if sound can't be played
+ pass
+
+
def main():
parser = argparse.ArgumentParser(
- description="Release documentation to asf-staging branch",
+ description="Promote documentation from asf-staging to asf-site
branch",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
parser.add_argument(
'--no-push',
action='store_true',
- help='Build and commit but don\'t push to remote'
+ help='Perform all steps except the final git push'
)
parser.add_argument(
'--commit-message',
type=str,
- default='Deploy documentation staging',
- help='Custom commit message (default: "Deploy documentation staging")'
+ help='Not used (kept for consistency with release-docs-stage.py)'
)
args = parser.parse_args()
script_dir = Path(__file__).parent
project_root = script_dir.parent
- docs_dir = project_root / 'juneau-docs'
- build_dir = docs_dir / 'build'
print("=" * 79)
- print("Release Documentation to Staging Branch")
+ print("Promote Documentation to Production (asf-site branch)")
print("=" * 79)
print()
-
- # Step 1: Run build-docs.py with --staging flag
- print("Step 1: Building documentation for staging...")
- build_script = script_dir / 'build-docs.py'
- if not build_script.exists():
- print(f"❌ ERROR: {build_script} not found")
- sys.exit(1)
-
- if not run_command(
- [sys.executable, str(build_script), '--staging'],
- cwd=project_root,
- description="Building documentation for staging"
- ):
- print("\n❌ Documentation build failed. Aborting.")
- sys.exit(1)
-
- if not build_dir.exists():
- print(f"❌ ERROR: Build directory not found at {build_dir}")
- print(" Documentation build may have failed.")
- sys.exit(1)
-
- # Step 2: Create temp directory and checkout asf-staging
- print("\nStep 2: Setting up temporary directory with asf-staging
branch...")
+ print("⚠️ WARNING: This will promote documentation from asf-staging to
asf-site,")
+ print(" making it live on the production website.")
+ print()
# Get git remote URL
remote_url = get_git_remote_url()
@@ -140,11 +185,11 @@ def main():
sys.exit(1)
# Create temp directory
- temp_dir = Path(tempfile.mkdtemp(prefix='juneau-docs-staging-'))
+ temp_dir = Path(tempfile.mkdtemp(prefix='juneau-docs-promote-'))
print(f"Temporary directory: {temp_dir}")
try:
- # Clone repository to temp directory
+ # Step 1: Clone repository to temp directory
if not run_command(
["git", "clone", remote_url, str(temp_dir)],
description="Cloning repository to temp directory"
@@ -152,67 +197,32 @@ def main():
print("\n❌ Failed to clone repository")
sys.exit(1)
- # Fetch asf-staging branch
+ # Step 2: Fetch asf-staging branch
if not run_command(
["git", "fetch", "origin", "asf-staging"],
cwd=temp_dir,
- check=False, # Don't fail if branch doesn't exist yet
description="Fetching asf-staging branch"
):
- print("⚠ Warning: Could not fetch asf-staging (branch may not
exist yet)")
+ print("\n❌ Failed to fetch asf-staging branch")
+ sys.exit(1)
- # Checkout or create asf-staging branch
- result = subprocess.run(
- ["git", "checkout", "-B", "asf-staging", "origin/asf-staging"],
+ # Step 3: Switch to detached HEAD at origin/asf-staging
+ if not run_command(
+ ["git", "switch", "--detach", "origin/asf-staging"],
cwd=temp_dir,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- text=True
- )
-
- if result.returncode != 0:
- # Branch doesn't exist, create it
- if not run_command(
- ["git", "checkout", "-b", "asf-staging"],
- cwd=temp_dir,
- description="Creating new asf-staging branch"
- ):
- print("\n❌ Failed to create asf-staging branch")
- sys.exit(1)
- else:
- print("✅ Checked out asf-staging branch")
-
- # Step 3: Remove all existing files (except .git)
- print("\nStep 3: Cleaning temp directory...")
- for item in temp_dir.iterdir():
- if item.name != '.git':
- if item.is_dir():
- shutil.rmtree(item)
- else:
- item.unlink()
- print("✅ Cleaned temp directory")
-
- # Step 4: Copy build directory contents
- print("\nStep 4: Copying build directory contents...")
- for item in build_dir.iterdir():
- dest = temp_dir / item.name
- if item.is_dir():
- shutil.copytree(item, dest)
- else:
- shutil.copy2(item, dest)
- print(f"✅ Copied contents from {build_dir} to {temp_dir}")
-
- # Step 5: Add and commit changes
- print("\nStep 5: Committing changes...")
+ description="Switching to detached HEAD at origin/asf-staging"
+ ):
+ print("\n❌ Failed to switch to asf-staging branch")
+ sys.exit(1)
- # Set git user (use current user's git config or defaults)
+ # Step 4: Set git user (use current user's git config or defaults)
try:
result = subprocess.run(
["git", "config", "--get", "user.name"],
capture_output=True,
text=True
)
- git_user = result.stdout.strip() if result.returncode == 0 else
"Documentation Builder"
+ git_user = result.stdout.strip() if result.returncode == 0 else
"Documentation Promoter"
result = subprocess.run(
["git", "config", "--get", "user.email"],
@@ -221,7 +231,7 @@ def main():
)
git_email = result.stdout.strip() if result.returncode == 0 else
"[email protected]"
except Exception:
- git_user = "Documentation Builder"
+ git_user = "Documentation Promoter"
git_email = "[email protected]"
run_command(
@@ -235,57 +245,33 @@ def main():
description="Setting git user email"
)
- # Add all files
- if not run_command(
- ["git", "add", "-A"],
- cwd=temp_dir,
- description="Adding all files"
- ):
- print("\n❌ Failed to add files")
- sys.exit(1)
-
- # Check if there are changes to commit
- result = subprocess.run(
- ["git", "diff", "--staged", "--quiet"],
- cwd=temp_dir
- )
-
- if result.returncode != 0:
- # There are changes, commit them
- if not run_command(
- ["git", "commit", "-m", args.commit_message],
- cwd=temp_dir,
- description=f"Committing changes: {args.commit_message}"
- ):
- print("\n❌ Failed to commit changes")
- sys.exit(1)
- else:
- print("ℹ️ No changes to commit")
-
- # Step 6: Push to remote (if not --no-push)
+ # Step 5: Push to asf-site branch (if not --no-push)
if not args.no_push:
- print("\nStep 6: Pushing to remote asf-staging branch...")
+ print("\nStep 5: Pushing to remote asf-site branch...")
if not run_command(
- ["git", "push", "origin", "asf-staging", "--force"],
+ ["git", "push", "origin", "HEAD:asf-site", "--force"],
cwd=temp_dir,
- description="Pushing to remote"
+ description="Pushing to asf-site branch"
):
- print("\n❌ Failed to push to remote")
+ print("\n❌ Failed to push to asf-site branch")
sys.exit(1)
else:
print("\n⏭️ Skipping push (--no-push flag set)")
- print(f" Changes are in: {temp_dir}")
+ print(f" Changes are ready in: {temp_dir}")
print(" You can manually push with:")
print(f" cd {temp_dir}")
- print(" git push origin asf-staging --force")
+ print(" git push origin HEAD:asf-site --force")
print("\n" + "=" * 79)
- print("✅ Documentation staging deployment complete!")
+ print("✅ Documentation promotion to production complete!")
print("=" * 79)
if args.no_push:
print(f"\nTemporary directory: {temp_dir}")
print("(This directory will not be automatically cleaned up)")
+ # Play success sound
+ play_sound(success=True)
+
except KeyboardInterrupt:
print("\n\n⚠️ Process interrupted by user")
sys.exit(1)