Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
What Is GitHub Actions? Your First CI with Workflows, Events, Jobs, Steps and Runners
GitHub Actions is a CI/CD platform that runs the YAML workflows under .github/workflows on runners in response to repository events. We write a first workflow that restores, builds and unit-tests the Bookstore API, try it locally with act and actionlint, and cover uses versus run, the run log, re-runs and the reasoning behind the Web API 31 workflow.
GitHub Actions Triggers: push, pull_request, workflow_dispatch, schedule and Filters
The events and filters under on decide when a workflow runs: branches, tags, paths and ! negation for push and pull_request, activity types, workflow_dispatch inputs, timezone-aware schedules, workflow_run, repository_dispatch and merge_group. With real output we show the trap of reading booleans via github.event.inputs, the trigger mistakes actionlint catches, and why act does not evaluate filters.
Expressions, Contexts and Variables in GitHub Actions: if, outputs and GITHUB_ENV
Expressions inside ${{ }} read values from contexts (github, env, vars, steps, needs, inputs…), use loose comparison and drive if: conditions. With act output we show why text outside ${{ }} in if is always true, the contains/fromJSON/hashFiles/case functions, moving data with GITHUB_OUTPUT and GITHUB_ENV, a test table with GITHUB_STEP_SUMMARY, and always versus !cancelled.
Jobs in GitHub Actions: needs, matrix, concurrency, timeouts and Service Containers
Jobs run in parallel by default; needs builds a DAG, and everything depending on a failed job is skipped. We split the Bookstore CI into format, unit-test (OS matrix), integration-test (PostgreSQL 16/17/18 service container), web-build and publish jobs and run it with act, covering include/exclude, fail-fast, max-parallel, concurrency, timeout-minutes, continue-on-error and the service command/entrypoint keys from April 2026.
.NET CI with GitHub Actions: setup-dotnet, global.json, NuGet Cache, Test Reports and Coverage
In a .NET 10 CI, pin the SDK with global.json and setup-dotnet@v6, restore with lock files and --locked-mode, key the NuGet cache to the lock hash, and gate with dotnet format and -warnaserror. We produce TRX and Cobertura reports with xUnit v3 on Microsoft.Testing.Platform, write them to the job summary and keep them with upload-artifact, and show act's v7 artifact limit and the Linux file-name pitfall with real output.
Angular CI with GitHub Actions: setup-node, npm ci, Lint, Vitest, Build Budgets and Playwright
In an Angular 22 CI, read the Node version from devEngines in package.json with setup-node@v7, install from the lock file with npm ci, run ng lint, Vitest and ng build with budgets, and run Playwright e2e with the API and a PostgreSQL service container. With act output we show why setup-node's automatic npm cache did not turn on in a monorepo, the lock drift error, a Node version mismatch and the effect of the browser cache.
GitHub Actions in a .NET + Angular Monorepo: Path Filters, Passing Artifacts and a Single Release Package
Path-filtered separate workflows leave required checks pending; in a monorepo, a single workflow with a changes job that decides via git diff and an always-running summary job (ci-ok) fixes that. We carry Angular's dist into the .NET publish's wwwroot with an artifact, produce a single release zip on a tag push, and explain artifact versus cache and a GitHub Release with gh release create, using act output.
Secrets, Variables and GITHUB_TOKEN Permissions in GitHub Actions
Secrets are defined at organization, repository and environment level, the narrowest level wins, and they are masked in logs by text matching; derived values such as base64 and fields read out of JSON are not masked. With fake secrets in act we show ::add-mask::, secrets in if conditions, variables and an empty GITHUB_TOKEN, and cover least privilege with permissions, the vulnerability-alerts scope from September 2026, and GitHub App tokens instead of PATs.
GitHub Actions Environments: Protection Rules, Approvals and Deployment History
An environment is a named target that applies protection rules (required reviewers, wait timers, branch/tag policies) before a job reaches a runner, exposes its secrets only to that job and keeps a deployment history. Bookstore's staging/production deploy workflow, plan limits for private repositories, deployment: false, per-environment concurrency and queue: max; where act and actionlint stop, with real output.
Deploying with GitHub Actions 1: Azure App Service with OIDC and Secretless Cloud Access
With OIDC, every job gets a signed, short-lived token from GitHub, and Azure verifies it against a federated credential and grants access without a client secret. id-token: write, sub claim formats, the July 2026 subject change with IDs, custom property claims, deploying to a staging slot, smoke testing and swapping; AWS and Google Cloud equivalents.
Deploying with GitHub Actions 2: Your Own Linux Server via SSH, Docker Images and GHCR
Build the image once in GitHub Actions, push it to GHCR and send only the digest to the server over SSH; the server pulls it with Docker Compose and restarts. metadata-action, build-push-action and the gha cache backend (28 s down to 4 s), actions/attest, pinning known_hosts and what happens when the host key changes, rollback with .env.previous; real runs with act.
Reducing Duplication in GitHub Actions: Composite Actions, Reusable Workflows and YAML Anchors
Use a composite action to share steps, a reusable workflow to share jobs that need environments and approvals, and YAML anchors to repeat a block within one file. Bookstore's dotnet-ci action and reusable-deploy workflow under act; the risks of secrets: inherit, environment secrets, the 10-level/50-workflow limits, the $/ syntax, versioning and why merge keys are not supported.