Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Codespaces Configuration

This directory contains the GitHub Codespaces dev container configuration.

## Port Visibility

Port 8080 may show as **private** in the PORTS panel, but this is usually not an issue - you can still access Jenkins using the forwarded URL.

**Note:** The port visibility label in the UI can be misleading. Even when marked as "private", the Jenkins URL provided in the welcome message will work in your browser. Only change visibility to "public" if you need to share the URL with others.

### Manual Steps (if needed for sharing):
1. Open the **PORTS** panel at the bottom of VS Code (next to TERMINAL)
2. Find port **8080** in the list
3. **Right-click** on port 8080
4. Select **Port Visibility** → **Public**

### Technical Details

The `devcontainer.json` includes `"visibility": "public"` for port 8080, but GitHub Codespaces may not always apply this setting automatically. The setup script attempts to set visibility using the GitHub CLI, but this is optional since Codespaces authentication allows private port access.

## Files

- **devcontainer.json** - Dev container specification
- **setup.sh** - Initialization script (installs yq, configures URLs, creates welcome message)
- **welcome.txt** - Generated welcome message (not in git, created at runtime)
- **README.md** - This file

## Accessing Jenkins

After starting a tutorial with `docker compose --profile <name> up -d`:
- Jenkins URL: `https://<codespace>-8080.<domain>` (shown in PORTS panel)
- Default credentials: admin/admin

**Important:** Open Jenkins in a regular browser tab, not the VS Code preview pane. The preview may show "Please reopen the preview" due to Jenkins security headers. Click the globe icon 🌐 in the PORTS panel or copy the URL to your browser.

## Troubleshooting

**Port 8080 refuses connection:**
- Verify Jenkins is running: `docker compose ps`
- Check logs: `docker compose logs jenkins_controller`
- Wait 1-2 minutes for Jenkins to fully start
- Port visibility (private/public) should not affect access for the Codespace owner

**Welcome message not showing:**
- Run: `source ~/.bashrc` in your terminal
- Or open a new terminal window

**yq not found:**
- Run: `bash .devcontainer/setup.sh` manually
54 changes: 54 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "Jenkins Quickstart Tutorials",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",

"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "27.0",
"dockerDashComposeVersion": "v2"
},
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "2.62"
}
},

"onCreateCommand": "bash .devcontainer/setup.sh",
"postStartCommand": "cat .devcontainer/welcome.txt 2>/dev/null || echo 'Run: .devcontainer/setup.sh for tutorial instructions'",

"forwardPorts": [8080, 3000, 5000],

"portsAttributes": {
"8080": {
"label": "Jenkins Controller",
"onAutoForward": "openBrowser",
"protocol": "http",
"visibility": "public"
},
"3000": {
"label": "Application Port (Node/Android/Go)",
"onAutoForward": "notify",
"protocol": "http"
},
"5000": {
"label": "Application Port (Multi/.NET)",
"onAutoForward": "notify",
"protocol": "http"
}
},

"customizations": {
"vscode": {
"extensions": [
"ms-azuretools.vscode-docker",
"redhat.vscode-yaml"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
}
}
},

"remoteUser": "vscode",

"updateContentCommand": "echo 'Container updated successfully'"
}
121 changes: 121 additions & 0 deletions .devcontainer/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# GitHub Codespaces setup script for Jenkins Quickstart Tutorials
# This script configures the Codespace environment and prepares Jenkins URL configuration

set -Eeuo pipefail # Exit on error, undefined variables, pipe failures

echo "================================"
echo "Setting up Jenkins Tutorials Environment"
echo "================================"

# Install yq (YAML processor) - required for JCaC configuration
echo "📦 Installing yq YAML processor..."
YQ_VERSION="${YQ_VERSION:-v4.44.3}"
Copy link

Copilot AI Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The yq version is hardcoded here but also managed in updatecli/updatecli.d/devcontainer.yaml. This creates a potential inconsistency. Consider making this script read the version from devcontainer.json or another single source of truth to avoid version drift.

Suggested change
YQ_VERSION="${YQ_VERSION:-v4.44.3}"
# Extract yq version from devcontainer.json
if ! command -v jq &> /dev/null; then
echo "❌ Error: jq is required to parse devcontainer.json but was not found. Please install jq and rerun this script."
exit 1
fi
YQ_VERSION=$(jq -r '.customizations.yqVersion // empty' .devcontainer/devcontainer.json)
if [ -z "$YQ_VERSION" ]; then
echo "❌ Error: yqVersion not found in .devcontainer/devcontainer.json (expected at .customizations.yqVersion)."
exit 1
fi

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Thanks for the review! I understand the concern about version consistency, but this design is intentional.

How version management works:

This repository uses UpdateCLI for automated dependency tracking. The yq version is managed in updatecli/updatecli.d/devcontainer.yaml:

sources:
  yq:
    name: Get latest yq version
    kind: githubrelease
    spec:
      owner: mikefarah
      repository: yq
      versionFilter:
        kind: semver

targets:
  devcontainer_setup_yq:
    name: Update yq version in setup.sh
    kind: file
    spec:
      file: .devcontainer/setup.sh
      matchpattern: 'YQ_VERSION="\${YQ_VERSION:-(.*)}\\'
      replacepattern: 'YQ_VERSION="\${YQ_VERSION:-{{ source \"yq\" }}}\\'

Why not read from devcontainer.json?

  1. Circular dependency: Your suggestion requires jq to be pre-installed in the devcontainer base image. If jq is missing, the setup fails entirely.

  2. Single source of truth: UpdateCLI IS the single source of truth. It automatically updates this file when new yq versions are released.

  3. Simplicity: Direct version variable is easier to override (YQ_VERSION=v4.50.0 bash setup.sh) and debug.

  4. No runtime parsing: The current approach doesn't require parsing JSON at runtime, making it faster and more reliable.

The UpdateCLI workflow:

  1. UpdateCLI monitors yq releases
  2. When a new version is released, UpdateCLI opens a PR updating .devcontainer/setup.sh
  3. CI validates the change
  4. Maintainers merge the automated PR

This is a common pattern for managing dependencies in infrastructure-as-code. The version appears "hardcoded" but is actually managed by automation.

See updatecli/updatecli.d/devcontainer.yaml for the full configuration.

YQ_URL="https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64"

# Try wget first, fall back to curl if unavailable
if command -v wget &> /dev/null; then
sudo wget -qO /usr/local/bin/yq "${YQ_URL}"
elif command -v curl &> /dev/null; then
sudo curl -fsSL -o /usr/local/bin/yq "${YQ_URL}"
else
echo "❌ Error: Neither wget nor curl found. Cannot download yq."
exit 1
fi

sudo chmod a+x /usr/local/bin/yq
yq --version

# Verify Docker is available
echo "🐳 Verifying Docker installation..."
docker --version
docker compose version

# Create secrets directory if it doesn't exist
echo "📁 Creating secrets directory..."
mkdir -p ./secrets

# Run Codespaces URL configuration script
if [ -f "./dockerfiles/codespacesURL.sh" ]; then
echo "🔧 Configuring Jenkins URLs for Codespaces..."
chmod +x ./dockerfiles/codespacesURL.sh
./dockerfiles/codespacesURL.sh
else
echo "⚠️ Warning: codespacesURL.sh not found, skipping URL configuration"
fi

# Create welcome message for future terminal sessions
WELCOME_FILE=".devcontainer/welcome.txt"
cat > "${WELCOME_FILE}" << 'WELCOME_EOF'

================================
🚀 Jenkins Quickstart Tutorials
================================

Available tutorial profiles:
• default : Basic Jenkins with SSH agent
• maven : Jenkins + Maven build environment
• python : Jenkins + Python development
• node : Jenkins + Node.js/npm
• multi : Multibranch pipeline example
• android : Android development
• golang : Go development
• cpp : C++ development
• dotnet : .NET development
• wizard : Jenkins setup wizard (learning)

Quick Start:
docker compose --profile <profile-name> up -d

Examples:
docker compose --profile maven up -d
docker compose --profile node up -d

To build locally:
docker compose -f build-docker-compose.yaml --profile <profile-name> up -d

WELCOME_EOF

# Add Jenkins URL based on environment
if [ -n "${CODESPACE_NAME:-}" ] && [ -n "${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:-}" ]; then
echo "Jenkins will be accessible at:" >> "${WELCOME_FILE}"
echo " https://${CODESPACE_NAME}-8080.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}" >> "${WELCOME_FILE}"
else
echo "Jenkins will be accessible at:" >> "${WELCOME_FILE}"
echo " http://localhost:8080" >> "${WELCOME_FILE}"
fi

echo "" >> "${WELCOME_FILE}"
echo "Default credentials: admin/admin" >> "${WELCOME_FILE}"
echo "================================" >> "${WELCOME_FILE}"
echo "" >> "${WELCOME_FILE}"

# Display the welcome message
cat "${WELCOME_FILE}"

echo "✅ Setup Complete! Welcome message saved to ${WELCOME_FILE}"
echo ""

# Add welcome message to .bashrc so it shows on every new terminal
# Use git rev-parse to find repo root dynamically instead of hardcoding path
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
WELCOME_PATH="${REPO_ROOT}/.devcontainer/welcome.txt"

if ! grep -q "Jenkins Quickstart Tutorials Welcome" ~/.bashrc; then
echo "" >> ~/.bashrc
echo "# Jenkins Quickstart Tutorials Welcome" >> ~/.bashrc
echo "if [ -f \"${WELCOME_PATH}\" ]; then" >> ~/.bashrc
echo " cat \"${WELCOME_PATH}\"" >> ~/.bashrc
echo "fi" >> ~/.bashrc
fi

# Set port 8080 visibility to public using gh CLI (if in Codespaces)
if [ -n "${CODESPACE_NAME:-}" ]; then
echo "🔓 Setting port 8080 visibility to public..."
# Check if gh CLI is authenticated before attempting to set port visibility
if gh auth status &>/dev/null; then
gh codespace ports visibility 8080:public -c "${CODESPACE_NAME}" 2>/dev/null || echo "⚠️ Could not set port visibility automatically. Please set port 8080 to public manually in the PORTS panel."
else
echo "⚠️ gh CLI not authenticated. Please set port 8080 to public manually in the PORTS panel."
fi
fi
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
#
# Note: GitPod configuration (.gitpod.yml and .gitpod.Dockerfile) is in legacy mode
# as we've migrated to GitHub Codespaces. GitPod configs will not receive dependency
# updates but remain functional for users who prefer GitPod.

version: 2
# Enable version updates for GitHub Actions workflows
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ secrets/*
03_maven_tutorial/secrets/*
.tutorial_running.txt

# Local development files
CLAUDE.md
CONTEXT.md
.devcontainer/welcome.txt
Loading
Loading