Code of the Day
AdvancedCI/CD for Automation

Lab: Deploy workflow

Write a GitHub Actions workflow that runs a Python report script on a weekly schedule, injects a Secret as an environment variable, and uploads the output as a downloadable artifact.

Lab · optionalWorkflowAdvanced30 min
Recommended first
By the end of this lesson you will be able to:
  • Write a workflow with both a cron schedule trigger and a workflow_dispatch trigger
  • Set up Python with pip caching and run a report generation script
  • Inject a GitHub Secret as an environment variable for the script
  • Upload a reports directory as a downloadable artifact using actions/upload-artifact@v4
  • Optionally gate the job behind a deployment environment with a required reviewer

This lab walks through writing a complete GitHub Actions workflow for a real automation use case: a weekly data report that needs an API key and produces output files that stakeholders can download. The result is a .github/workflows/ file you can adapt directly to any scheduled report pipeline.

This lab covers YAML configuration. There is no in-browser Python runner here — the runnable cells show the Python side of the pipeline so you can verify the script logic locally before wiring it into Actions.

The Python script

Before writing the workflow, confirm the script works locally. A minimal generate_report.py reads an API key from the environment and writes output to a reports/ directory:

Python — editable, runs in your browser

The script never prints the full key — only the first eight characters for confirmation. In production, avoid even that. The full key is only ever in memory during the API call itself.

Step 1 — Choose triggers

The workflow should run automatically every Monday at 09:00 UTC and also be triggerable manually when you need an out-of-cycle run:

on:
  schedule:
    - cron: "0 9 * * 1"   # Monday 09:00 UTC
  workflow_dispatch:        # Manual "Run workflow" button

0 9 * * 1 breaks down as: minute 0, hour 9, any day-of-month, any month, day-of-week 1 (Monday). GitHub runs scheduled workflows within a few minutes of the target time; do not rely on exact timing for latency-sensitive operations.

workflow_dispatch adds a button to the Actions tab in the GitHub UI and enables gh workflow run data-report.yml from the CLI. Both use the same job definition.

Step 2 — Check out, set up Python, install dependencies

jobs:
  generate-report:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"

      - name: Install dependencies
        run: pip install -r requirements.txt

cache: "pip" tells setup-python to cache the pip download cache between runs, keyed on the hash of requirements.txt. Cold runs download everything; warm runs (the common case) restore from cache in seconds.

actions/checkout@v4 is always the first step — without it the workspace is empty and requirements.txt does not exist for the install step to read.

Step 3 — Run the script with the Secret

      - name: Generate report
        run: python generate_report.py
        env:
          API_KEY: ${{ secrets.API_KEY }}

The env: block at the step level injects the secret as an environment variable for that step only. os.environ["API_KEY"] in the script reads it at runtime.

The value is masked in the log: if generate_report.py accidentally prints the key, GitHub replaces it with ***. The masking is applied after the fact — do not rely on it as a substitute for not printing secrets.

Step 4 — Upload the output as an artifact

      - name: Upload report artifact
        uses: actions/upload-artifact@v4
        with:
          name: weekly-report-${{ github.run_id }}
          path: reports/
          retention-days: 30

actions/upload-artifact@v4 compresses and stores the reports/ directory. After the workflow completes, it appears under Summary → Artifacts in the Actions UI. Team members can download it without needing repository access, just Actions read permission.

retention-days: 30 keeps the artifact for 30 days before automatic deletion. Adjust based on compliance requirements — the maximum is 90 days on the free tier.

The complete workflow file

Save this as .github/workflows/data-report.yml:

name: Weekly data report

on:
  schedule:
    - cron: "0 9 * * 1"
  workflow_dispatch:

jobs:
  generate-report:
    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Generate report
        run: python generate_report.py
        env:
          API_KEY: ${{ secrets.API_KEY }}

      - name: Upload report artifact
        uses: actions/upload-artifact@v4
        with:
          name: weekly-report-${{ github.run_id }}
          path: reports/
          retention-days: 30

Step 5 (optional) — Add a production environment gate

For reports that touch sensitive data or external systems, add a deployment environment with a required reviewer. Change the job header:

jobs:
  generate-report:
    runs-on: ubuntu-latest
    environment: production

Then in Settings → Environments → production → Protection rules, enable Required reviewers and add the team members who must approve before the job runs. The job will pause at the environment gate and send a notification to reviewers — no code change required.

Environment secrets work the same way: ${{ secrets.API_KEY }} reads from the production environment's secret store rather than the repository-level store, so staging and production can use different keys with no workflow changes.

Use workflow_dispatch to test the workflow on demand before the first scheduled run. Push the workflow file, navigate to Actions → Weekly data report → Run workflow, and verify the artifact appears. Only then wait for the Monday morning run to confirm the schedule works.

What you built

A production-ready scheduled automation workflow with:

  • A cron schedule plus a manual dispatch trigger.
  • Python setup with pip caching to keep warm runs fast.
  • A GitHub Secret injected as an environment variable — never hard-coded.
  • Artifact upload so report files are downloadable from the Actions UI.
  • An optional environment gate with a required human reviewer for production use.

This pattern applies to any scheduled Python automation: data ingestion, model retraining, compliance reports, infrastructure audits. The structure stays the same; only the script and the artifact path change.

Where to go next

You have completed the CI/CD for Automation module. The workflow track continues with the containerised-workflows module — packaging your pipeline in Docker so the CI environment is identical to your development environment.

Finished reading? Mark it complete to track your progress.

On this page