Replace the placeholder token from lesson 401 with a real GitHub Personal Access Token stored as GITHUB_TOKEN and learn every authentication pattern used in production REST API work: Bearer token headers, API key headers, and query-string tokens. Credentials are loaded from .env via load_dotenv() and os.environ.get() ā never hardcoded. Operational config (base URL, timeout) stays in config.yaml via load_config(). The lesson closes with a reusable requests.Session for authenticated sessions and explicit 401/403 error handling.
Create a GitHub Personal Access Token and store it as GITHUB_TOKEN in a new .env file. Fine-grained PATs are preferred for new use, but classic PATs also work for this lesson. GITHUB_TOKEN is the standard environment variable name for GitHub credentials ā the same name used by GitHub Actions and most CI/CD platforms. Scripts load it directly via os.environ.get('GITHUB_TOKEN') after calling load_dotenv(), keeping secrets separate from operational config (base URL, timeout) which stays in config.yaml.
mkdir -p ~/devops-python/lesson-402cd ~/devops-python/lesson-402source ~/devops-python/lesson-101/devops-env/bin/activate# --- Create your GitHub PAT (do this in your browser) ---# 1. Go to: github.com ā Settings ā Developer settings ā Personal access tokens# 2. Create either a fine-grained PAT (preferred) or a classic PAT# 3. Note: devopspath-lesson-402 Expiration: 7 days# 4. Grant only the minimum access needed for public-repo examples# 5. Click 'Generate token' ā copy the token now (fine-grained tokens start with github_pat_, classic tokens with ghp_; shown only once)# After copying your token, replace ghp_REPLACE_WITH_YOUR_TOKEN in the command below:# Lesson 402 ā replace the placeholder with your real GitHub PAT.
# Never commit this file. It is listed in .gitignore.
GITHUB_TOKEN=ghp_REPLACE_WITH_YOUR_TOKEN.env
.env.local
__pycache__/
*.pycenvironment: development
api_base_url: https://api.github.com
api_timeout: 10
debug: true
max_retries: 3
deploy_host: localhost
deploy_port: 8080python3 -c "
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
sys.path.insert(0, str(Path.home() / 'devops-python' / 'lesson-304'))
from layered_config import load_config
load_dotenv()
config, sources = load_config(
config_file=Path('config.yaml'),
env_file=Path('.env'),
)
token = os.environ.get('GITHUB_TOKEN', '')
has_token = bool(token) and not token.startswith('ghp_REPLACE')
print(f'api_base_url: {config.api_base_url} [source: {sources[\"api_base_url\"]}]')
print(f'api_timeout: {config.api_timeout}s [source: {sources[\"api_timeout\"]}]')
print(f'GITHUB_TOKEN set: {has_token} ({len(token)} chars)')
if not has_token:
print()
print('ACTION NEEDED: edit .env and replace ghp_REPLACE_WITH_YOUR_TOKEN with your real GITHUB_TOKEN.')
"A GitHub Personal Access Token authenticates your API requests as your GitHub account. A valid token upgrades your rate limit from 60 to 5,000 requests per hour and grants access according to the permissions you configured. Fine-grained PATs (which start with github_pat_) are preferred for new use because they let you scope access more tightly; classic PATs (which start with ghp_) also work for these examples. Store the token immediately in .env as GITHUB_TOKEN because GitHub shows it only once when created. Using GITHUB_TOKEN as the env var name is intentional: it is the standard name used by GitHub Actions, many CI/CD platforms, and real DevOps tooling. Scripts load it with load_dotenv() + os.environ.get('GITHUB_TOKEN', '') ā credentials load from the environment, operational settings (base URL, timeout) load from config.yaml. The .gitignore entry for .env is the critical safety layer: it prevents git add . from staging the file that contains your credentials.
The python3 verification command prints api_base_url: https://api.github.com, api_timeout: 10s, and GITHUB_TOKEN set: True (<n> chars).
A real GitHub PAT typically starts with either ghp_ (classic) or github_pat_ (fine-grained).
If the placeholder is still in place, the script prints the ACTION NEEDED message.