Skip to content
Draft
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
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,17 @@ updates:
update-types:
- "minor"
- "patch"

# git-cliff CLI (used by 'mage changelog'), pinned via a Cargo manifest so the
# version is visible to Dependabot and security scanners.
- package-ecosystem: "cargo"
directory: "/tools/git-cliff"
schedule:
interval: "weekly"
cooldown:
default-days: 7
groups:
dependabot-cargo-tools:
applies-to: version-updates
patterns:
- "*"
102 changes: 102 additions & 0 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: "Prepare release"

# Manually triggered. Generates the changelog draft on a release branch and
# pushes it. It does NOT open a PR (that needs auth we avoid) -- a maintainer
# opens the PR from the pushed branch, curates the draft, and merges it. Tagging
# happens afterwards in the "Release" workflow. See
# docs/developer/how-to/releasing.md.
on:
workflow_dispatch:

# Don't run two release preparations at once.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

permissions: {}

jobs:
prepare-release:
name: "Prepare release branch"
runs-on: ubuntu-latest
permissions:
contents: write # Push the release branch.
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# git-cliff needs full history and tags to compute the next version.
fetch-depth: 0
persist-credentials: false

- name: Get go tools
uses: ./.github/getgotools

- name: Read pinned git-cliff version
id: gitcliff
run: |
set -euo pipefail
version="$(sed -n 's/^[[:space:]]*git-cliff = "=\([0-9.]*\)".*/\1/p' tools/git-cliff/Cargo.toml)"
if [ -z "$version" ]; then
echo "could not read git-cliff version from tools/git-cliff/Cargo.toml" >&2
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Install git-cliff
uses: taiki-e/install-action@bffeee26d4db9be238a4ea78d8826604ebcb594d # v2.82.5
with:
tool: git-cliff@${{ steps.gitcliff.outputs.version }}

- name: Generate changelog draft
run: mage changelog

- name: Push release branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
run: |
set -euo pipefail

# Nothing to release: `mage changelog` left CHANGELOG.md unchanged (no new entries since
# the last release). Report it in the job summary and stop -- this is not a failure.
if git diff --quiet -- CHANGELOG.md; then
{
echo "### Nothing to release"
echo ""
echo "No new changelog entries since the last release, so no release branch was created."
} >> "$GITHUB_STEP_SUMMARY"
echo "Nothing to release: CHANGELOG.md is unchanged."
exit 0
fi

version="$(sed -n 's/^## \[\([0-9.]*\)\].*/\1/p' CHANGELOG.md | head -1)"
if [ -z "$version" ]; then
echo "no '## [X.Y.Z]' heading found in CHANGELOG.md" >&2
exit 1
fi
branch="release/v${version}"

# Refuse to clobber an existing release branch (e.g. a prior prepare-release run that
# has not been merged or cleaned up yet).
if git ls-remote --exit-code --heads origin "refs/heads/${branch}" >/dev/null 2>&1; then
echo "Release branch '${branch}' already exists on the remote; merge or delete it before preparing again." >&2
exit 1
fi

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git switch -c "$branch"
git add CHANGELOG.md
git commit -m "chore: prepare release v${version}"
git push "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" "$branch"
Comment on lines +90 to +92

# Build the create-PR URL from the current server/repo so it points at this repo (fork or
# upstream). GitHub's pull/new/<branch> form targets the repo's default branch as the base.
pr_url="${SERVER_URL}/${REPO}/pull/new/${branch}"
{
echo "Pushed branch \`$branch\` with the v${version} changelog draft."
echo ""
echo "**[Open the pull request →](${pr_url})**, curate the changelog into user-facing notes, then merge."
} >> "$GITHUB_STEP_SUMMARY"
echo "Open a PR: ${pr_url}"
83 changes: 83 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: "Release"

# Runs on every push to main. When CHANGELOG.md carries a new version, tags it
# (via the idempotent `mage release`), pushes the tag, and publishes a GitHub
# Release with that version's changelog notes. Normal merges are a no-op because
# the changelog's top version is already tagged. See docs/developer/how-to/releasing.md.
on:
push:
branches: [main]

# Don't run two releases at once.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

permissions: {}

jobs:
release:
name: "Tag release"
runs-on: ubuntu-latest
permissions:
contents: write # Push the release tag.
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# Need full history and tags to detect existing releases and tag HEAD.
fetch-depth: 0
persist-credentials: false

- name: Get go tools
uses: ./.github/getgotools

- name: Create release tag from changelog
run: |
# Annotated tags (git tag -a) need a committer identity, which the runner lacks.
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
mage release

- name: Publish release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
version="$(sed -n 's/^## \[\([0-9.]*\)\].*/\1/p' CHANGELOG.md | head -1)"
if [ -z "$version" ]; then
echo "no '## [X.Y.Z]' heading found in CHANGELOG.md" >&2
exit 1
fi
tag="v${version}"

# Push the tag if the remote lacks it. Tag push is kept separate from publishing the
# release so a rerun can finish a half-done release that pushed the tag but never
# published (e.g. a prior run that failed between the two steps).
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
echo "Tag \`${tag}\` already on remote." >> "$GITHUB_STEP_SUMMARY"
else
git push "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" "refs/tags/${tag}"
echo "Pushed tag \`${tag}\`." >> "$GITHUB_STEP_SUMMARY"
fi

# If the GitHub Release already exists, there's nothing left to publish or warm.
if gh release view "${tag}" --repo "${REPO}" >/dev/null 2>&1; then
echo "Release \`${tag}\` already exists; nothing to publish." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi

# Release notes = this version's changelog section (its body, without the heading line).
awk '/^## \[/{n++; next} n==1' CHANGELOG.md > "${RUNNER_TEMP}/release-notes.md"
gh release create "${tag}" --repo "${REPO}" --title "${tag}" --notes-file "${RUNNER_TEMP}/release-notes.md"
echo "Published release \`${tag}\` with notes from CHANGELOG.md." >> "$GITHUB_STEP_SUMMARY"

# Warm the module proxy and pkg.go.dev so the new version is discoverable promptly.
# Upstream only: a fork declares the upstream module path in go.mod, so the proxy would
# reject a request under the fork's path -- warming there just errors. Best-effort.
if [ "${REPO}" = "microsoft/azure-linux-dev-tools" ]; then
module="$(awk '/^module /{print $2; exit}' go.mod)"
GOPROXY=https://proxy.golang.org go list -m "${module}@${tag}" \
|| echo "proxy warm deferred; it will index on first use"
curl -fsS -o /dev/null "https://pkg.go.dev/${module}@${tag}" || true
fi
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Changelog

<!-- markdownlint-disable-file MD013 MD024 -->

All notable changes to `azldev` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-03-18

First tagged preview release of `azldev`, the developer CLI for the
[Azure Linux](https://github.com/microsoft/azurelinux) distro.

### Added

- **Project and metadata management.** Scaffold a project with `azldev project
init` or `project new`, then parse, resolve, and query the TOML metadata
(`azldev.toml`) that defines Azure Linux. Configuration merges built-in
defaults with project and user-level (XDG) files, is fully validated, and is
published as a JSON Schema via `azldev config generate-schema`.
- **Component inspection and locking.** List and inspect components with `azldev
component list` and `component query`, and import new ones with `component
add`. Deterministic component fingerprints and per-component lock files keep
builds reproducible; `component update` refreshes them with `--check-only`,
`--bump`, freshness-based skipping, a progress bar, and upstream-staleness
detection. `component changed` and `component diff-sources` report what moved.
- **Source preparation and spec rendering.** `component prepare-sources` and
`component render` produce build-ready sources and specs through a
`mock`-based batch pipeline, synthesizing the git history that `rpmautospec`
needs and constructing dist-git from lock-file history. A rich overlay system
(spec search/replace, prepend/append lines, remove section or subpackage, file
and source replacement, per-file overlay files, and inline metadata)
customizes specs, with explicit release-calculation modes (`autorelease`,
`static`, and automatic). Source archives are fetched from lookaside caches.
- **Local package and image builds.** Build individual packages with `mock`
using `component build`, emitting RPMs and SRPMs into structured,
publish-channel-aware output directories. `azldev image` builds, customizes,
injects files into, boots, and runs LISA tests against Azure Linux images on a
local QEMU VM.
- **Package and repository queries.** Inspect binary package configuration with
`azldev package list` (including `--rpm-file`, debug-package synthesis, and
separate package/component group columns), and inspect or manage RPM
repositories with `azldev repo query`, backed by repo resources and repo-set
templates.
- **Command-line experience.** Shell completions for bash, zsh, fish, and
PowerShell; actionable hints on errors; global `--quiet`, `--verbose`, and
`--dry-run` flags with `table`, `json`, `csv`, and `markdown` output formats;
an embedded MCP server (`azldev advanced mcp`); and auto-generated CLI
reference documentation.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Azure Linux Dev Tools

[![Go Reference](https://pkg.go.dev/badge/github.com/microsoft/azure-linux-dev-tools.svg)](https://pkg.go.dev/github.com/microsoft/azure-linux-dev-tools)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)

Azure Linux Dev Tools is a collection of utilities useful for development
of the Azure Linux distro.

Expand Down Expand Up @@ -28,9 +31,12 @@ It supports:
1. Install `azldev`:

```console
go install github.com/microsoft/azure-linux-dev-tools/cmd/azldev@main
go install github.com/microsoft/azure-linux-dev-tools/cmd/azldev@latest
```

To pin a specific release instead of tracking the latest, replace `@latest`
with a version tag, e.g. `@v0.1.0`.

1. To ensure you can build using `mock` you must be a member of the `mock` group, e.g.:

```console
Expand Down Expand Up @@ -62,6 +68,10 @@ Please see our [Contribution Guidelines](./CONTRIBUTING.md) for our project.

For development setup and workflow, please consult our [Developer Guide](./docs/developer).

## Changelog

Notable changes for each release are recorded in the [changelog](./CHANGELOG.md).

## Getting Help

Have questions, found a bug, or need a new feature? Open an issue in our [GitHub repository](https://github.com/microsoft/azure-linux-dev-tools/issues/new/choose). For guidance on how to file an issue, see [how to report issues](https://aka.ms/azurelinux-reportissues).
Expand Down
81 changes: 81 additions & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
# git-cliff configuration for generating azldev's CHANGELOG.md from Conventional
# Commits. Run it via `mage changelog`; see docs/developer/how-to/releasing.md.
# Reference: https://git-cliff.org
#
# The generated entries are a *draft*: a human (or Copilot) prunes and rewords
# them into user-facing notes before a release. Internal commit types (docs,
# test, chore, build, ci, style, refactor, and dependency bumps) are skipped so
# the draft starts close to user-facing.

[changelog]
# The markdownlint-disable-file comment scopes two relaxations to CHANGELOG.md
# only (no repo-wide markdownlint config): MD013 because auto-generated entries
# are one line per commit and can exceed the line length, and MD024 because the
# Keep a Changelog format repeats '### Added'/'### Fixed' under every version.
header = """
# Changelog

<!-- markdownlint-disable-file MD013 MD024 -->

All notable changes to `azldev` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""
# Per-version section in Keep a Changelog style. `version` is unset when
# rendering unreleased commits, which yields an `## [Unreleased]` heading. The
# leading blank line keeps a blank line above each version heading (markdownlint
# MD022) when the section is prepended below the file header or another version.
body = """

{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {{ commit.message | split(pat="\n") | first | upper_first | trim }}\
{% endfor %}
{% endfor %}\
"""
# Collapse any run of 3+ newlines left by the template into a single blank line
# so the rendered changelog has no double blank lines (markdownlint MD012).
postprocessors = [{ pattern = '\n{3,}', replace = "\n\n" }]
trim = true
footer = ""

[git]
conventional_commits = true
# Keep non-conventional commits (e.g. early "README.md committed" bootstrap
# commits) instead of treating them as parse errors. They fall through to the
# catch-all `.*` skip parser below, so they're dropped quietly rather than
# emitting a "commit(s) skipped due to parse error(s)" warning during the
# full-history version bump.
filter_unconventional = false
# Drop commits that don't match a kept parser below (cuts internal noise).
filter_commits = true
# ...but never drop a breaking change, even if its type is skipped above.
protect_breaking_commits = true
# Only consider release tags of the form `vX.Y.Z`.
tag_pattern = '^v[0-9]+\.[0-9]+\.[0-9]+$'
sort_commits = "oldest"
commit_parsers = [
{ message = "^feat", group = "Added" },
{ message = "^fix", group = "Fixed" },
{ message = "^perf", group = "Changed" },
{ message = "^revert", group = "Removed" },
{ message = "^refactor", skip = true },
{ message = "^docs", skip = true },
{ message = "^test", skip = true },
{ message = "^chore", skip = true },
{ message = "^build", skip = true },
{ message = "^ci", skip = true },
{ message = "^style", skip = true },
# Skip anything that didn't match a kept type above.
{ message = ".*", skip = true },
]
12 changes: 12 additions & 0 deletions cmd/azldev/azldev.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Command azldev is a developer tool for working on the Azure Linux distro.
//
// It parses, resolves, and queries the TOML-based metadata that defines Azure
// Linux, prepares component sources for building with mock, fetches source
// archives from lookaside caches, and offers convenience utilities for
// locally building individual packages and images.
//
// Install the latest release with:
//
// go install github.com/microsoft/azure-linux-dev-tools/cmd/azldev@latest
//
// Run "azldev --help" for usage information, or see the user guide under docs/user.
package main

import (
Expand Down
1 change: 1 addition & 0 deletions docs/developer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ To get started developing `azldev`, review the [Getting Started guide](./how-to/
* [Submitting Pull Requests](./how-to/pull-requests.md)
* [Configuration management](./how-to/config-management.md)
* [Add go tools](./how-to/add-go-tool.md)
* [Cut a release](./how-to/releasing.md)

## Reference

Expand Down
Loading
Loading