# Tag Publish
## Publishing
The main goals of Tag Publish offer the commands to publish the project,
Using a tag, a stabilization branch, a feature branch or a pull request.
When possible it can do a secret-less publishing (privileged in defaults), if it's not possible the login should be done before the publishing.
See the [documentation](https://github.com/camptocamp/c2cciutils/wiki/Publishing).
## Startup
Set the permissions:
```yaml
permissions:
# To publish Docker images on GHCR and on npm.pkg.github.com
packages: write
# To publish Python packages using OIDC
id-token: write
# To publish Helm charts and send repository dispatch notifications
contents: write
```
Install the package in the worklow:
```yaml
- name: Install tag-publish
run: pip install tag-publish
```
Do the publishing:
```yaml
- name: Publish
run: tag-publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
## New version
To create a new version you should create a Git tag with the version number.
## New stabilization branch
To create a new minor version you just should run `tag-publish-new --version=<version>`.
This will create the stabilization branch and will create a new pull request to update
the `SECURITY.md` file and the Renovate configuration.
This will also create the tags for the backport.
You are welcome to run `tag-publish-new --help` to see what's it's done.
Note that it didn't create a tag, you should do it manually.
To create a patch version you should just create tag.
## Kind of publishing
This tool can publish on different kind of versions:
- `tag`: Related to a Git tag.
- `default_branch`: Related to the default branch.
- `stabilization_branch`: Related to a stabilization branch (including the default branch).
- `feature_branch`: Related to a feature branch or a pull request.
We can also publish on different kind of versions like `rebuild` by using the `--type` argument.
## SECURITY.md
The `SECURITY.md` file should contain the security policy of the repository, especially the end of
support dates.
For compatibility with [`security.md`](https://github.com/sbrunner/security.md/) it should contain an array
with at least the columns `Version` and `Supported Until`. The `Version` column will contain the concerned
version.
The `Supported Until` will contain the date of end of support `dd/mm/yyyy`.
It can also contain the following sentences:
- `Unsupported`: no longer supported => no audit, no rebuild.
- `Best effort`: the support is ended, it is still rebuilt and audited, but this can be stopped without any notice.
- `To be defined`: not yet released or the date will be set related of another project release date (like for GeoMapFish).
See also [GitHub Documentation](https://docs.github.com/en/github/managing-security-vulnerabilities/adding-a-security-policy-to-your-repository)
## Configuration
The configuration file is `.github/publish.yaml`, the schema is `https://raw.githubusercontent.com/camptocamp/tag-publish/<version>/tag_publish/schema.json`.
### Dry run
Dry run publish: `GITHUB_REF=... tag-publish --dry-run ...`
### Python package to pypi repository
Minimum configuration:
```yaml
pypi:
packages:
- {}
```
If the file `~/.pypirc` didn't exists we will do a login using OpenId Connect (OIDC), see:
https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.
By default the package will be published only on tag, if you want to publish on stabilization branch you should add
a `versions` key with the list of versions you want to publish, that can be:
`rebuild` (specified with --type), `tag`, `default_branch`, `stabilization_branch`, `feature_branch`, `pull_request` (for pull request merge: number)
It we have a `setup.py` file, we will be in legacy mode:
When publishing, the version computed from arguments or `GITHUB_REF` is put in environment variable `VERSION`, thus you should use it in `setup.py`, example:
```python
VERSION = os.environ.get("VERSION", "1.0.0")
```
Also we consider that we use `poetry` with [poetry-dynamic-versioning](https://pypi.org/project/poetry-dynamic-versioning/) to manage the version, and [poetry-plugin-tweak-dependencies-version](https://pypi.org/project/poetry-plugin-tweak-dependencies-version/) to manage the dependencies versions.
Example of configuration:
```toml
[tool.poetry-dynamic-versioning]
enable = true
vcs = "git"
pattern = "^(?P<base>\\d+(\\.\\d+)*)"
format-jinja = """
{%- if env.get("VERSION_TYPE") == "default_branch" -%}
{{serialize_pep440(bump_version(base, 1), dev=distance)}}
{%- elif env.get("VERSION_TYPE") == "stabilization_branch" -%}
{{serialize_pep440(bump_version(base, 2), dev=distance)}}
{%- elif distance == 0 -%}
{{serialize_pep440(base)}}
{%- else -%}
{{serialize_pep440(bump_version(base), dev=distance)}}
{%- endif -%}
"""
```
Note that we can access to the environment variables `VERSION`,`VERSION_TYPE`.
Then by default:
- Tag with `1.2.3` => release `1.2.3`
- Commit on feature branch just do a validation
- Commit on `master` branch after the tag 1.3.0 => release `1.4.0.dev1`
- Commit on `1.3` branch after the tag 1.3.0 => release `1.3.1.dev1`
#### Authentication
If the file `~/.pypirc` exists we consider that we ar already logged in also
we will do the login with the `pypi` server with OpenID Connect (OIDC).
The OIDC login is recommended because it didn't needs any additional secrets,
but it need some configuration on pypi in the package,
see the [GitHub Documentation](https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi#adding-the-identity-provider-to-pypi).
The required permissions is `id-token: write`.
#### Integration if the package directly in a Docker image
To make it working in the `Dockerfile` you should have in the `poetry` stage:
```Dockerfile
ENV POETRY_DYNAMIC_VERSIONING_BYPASS=dev
RUN poetry export --extras=checks --extras=publish --output=requirements.txt \
&& poetry export --with=dev --output=requirements-dev.txt
```
And in the `run` stage
```Dockerfile
ARG VERSION=dev
RUN --mount=type=cache,target=/root/.cache \
POETRY_DYNAMIC_VERSIONING_BYPASS=${VERSION} python3 -m pip install --disable-pip-version-check --no-deps --editable=.
```
And in the `Makefile`:
```Makefile
VERSION = $(strip $(shell poetry version --short))
.PHONY: build
build: ## Build the Docker images
docker build --build-arg=VERSION=$(VERSION) --tag=$(GITHUB_REPOSITORY) .
```
### Docker image to registry
The minimal config is like this:
```yaml
docker:
images:
- name: camptocamp/tag-publish
```
If you want to use the GitHub token to be logged in on ghcr you should set `auto_login` to `True`, the
requires the permissions are `packages: write`.
With that the image initially named `camptocamp/tag-publish:latest` will be published on GitHub GHCR and on Docker hub.
The full config is like this:
```yaml
docker:
github_oidc_login: True
latest: True
images:
- # The base name of the image we want to publish
name:
repository:
<internal_name>:
# The fqdn name of the server if not Docker hub
server:
# List of kinds of versions you want to publish, that can be: rebuild (specified using --type),
# tag, stabilization_branch, feature_branch, pull_request (for pull request merge: number)
version:
# List of tags we want to publish interpreted with `format(version=version)`
# e.g. if you use `{version}-lite` when you publish the version `1.2.3` the source tag
# (that should be built by the application build) is `latest-lite`, and it will be published
# with the tag `1.2.3-lite`.
tags:
# If your images are published by different jobs you can separate them in different groups
# and publish them with `tag-publish --group=<group>`
group:
```
By default, the last line of the `SECURITY.md` file will be published (`docker`) with the tag
`latest`. Set `latest` to `False` to disable it.
#### Use Renovate to trigger a new build instead of the legacy rebuild
If the `ci/dpkg-versions.yaml` or `.github/dpkg-versions.yaml` file is present, the package list will be updated on publishing.
The versions will be updated by [GHCI](https://github.com/camptocamp/github-app-geo-project/) application.
### Node package to npm repository
Minimum configuration:
```yaml
node:
packages:
- {}
```
If the repository server is `npm.pkg.github.com` we will do a login using `GITHUB_TOKEN`.
To publish on `npm.pkg.github.com` you requires the permissions are `packages: write`, and the `id-token: write` for the provenance.
By default the package will be published only on tag.
### HELM
The minimal config is like this:
```yaml
helm:
packages:
- {}
```
This will publish the `helm` charts in the current folder using [chart releaser](https://github.com/helm/chart-releaser).
The artifacts will be attached to a GitHub release, and the `index.yaml` file will be updated in the `gh-pages` branch.
The required permission is `contents: write`.
Create the required `gh-pages` branch:
```bash
git checkout --orphan gh-pages
git reset --hard
git commit --allow-empty -m "Initialize gh-pages branch"
git push origin gh-pages
```
## Dispatch
The minimal config is like this:
```yaml
dispatch:
- {}
```
The required permission is `contents: write`.
This will create a repository dispatch of type `published` on own repository with the `content` e.g.:
```json
{
"version": "1.2.3",
"version_type": "tag",
"repository": "camptocamp/tag-publish",
"items": [
{
"type": "docker",
"image": "camptocamp/tag-publish",
"repository": "ghcr.io",
"tag": "1.2.3"
},
{
"type": "pypi",
"path": "."
},
{
"type": "helm",
"path": "."
}
]
}
```
## Contributing
Install the pre-commit hooks:
```bash
pip install pre-commit
pre-commit install --allow-missing-config
```
Raw data
{
"_id": null,
"home_page": "https://github.com/camptocamp/tag-publish",
"name": "tag-publish",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "ci",
"author": "Camptocamp",
"author_email": "info@camptocamp.com",
"download_url": "https://files.pythonhosted.org/packages/aa/cf/9672e5418c9a935cb95dc506c17eb75dca01be5d9d6b49d3da507a17abb4/tag_publish-0.13.3.tar.gz",
"platform": null,
"description": "# Tag Publish\n\n## Publishing\n\nThe main goals of Tag Publish offer the commands to publish the project,\nUsing a tag, a stabilization branch, a feature branch or a pull request.\n\nWhen possible it can do a secret-less publishing (privileged in defaults), if it's not possible the login should be done before the publishing.\n\nSee the [documentation](https://github.com/camptocamp/c2cciutils/wiki/Publishing).\n\n## Startup\n\nSet the permissions:\n\n```yaml\npermissions:\n # To publish Docker images on GHCR and on npm.pkg.github.com\n packages: write\n # To publish Python packages using OIDC\n id-token: write\n # To publish Helm charts and send repository dispatch notifications\n contents: write\n```\n\nInstall the package in the worklow:\n\n```yaml\n- name: Install tag-publish\n run: pip install tag-publish\n```\n\nDo the publishing:\n\n```yaml\n- name: Publish\n run: tag-publish\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n```\n\n## New version\n\nTo create a new version you should create a Git tag with the version number.\n\n## New stabilization branch\n\nTo create a new minor version you just should run `tag-publish-new --version=<version>`.\n\nThis will create the stabilization branch and will create a new pull request to update\nthe `SECURITY.md` file and the Renovate configuration.\n\nThis will also create the tags for the backport.\n\nYou are welcome to run `tag-publish-new --help` to see what's it's done.\n\nNote that it didn't create a tag, you should do it manually.\n\nTo create a patch version you should just create tag.\n\n## Kind of publishing\n\nThis tool can publish on different kind of versions:\n\n- `tag`: Related to a Git tag.\n- `default_branch`: Related to the default branch.\n- `stabilization_branch`: Related to a stabilization branch (including the default branch).\n- `feature_branch`: Related to a feature branch or a pull request.\n\nWe can also publish on different kind of versions like `rebuild` by using the `--type` argument.\n\n## SECURITY.md\n\nThe `SECURITY.md` file should contain the security policy of the repository, especially the end of\nsupport dates.\n\nFor compatibility with [`security.md`](https://github.com/sbrunner/security.md/) it should contain an array\nwith at least the columns `Version` and `Supported Until`. The `Version` column will contain the concerned\nversion.\nThe `Supported Until` will contain the date of end of support `dd/mm/yyyy`.\nIt can also contain the following sentences:\n\n- `Unsupported`: no longer supported => no audit, no rebuild.\n- `Best effort`: the support is ended, it is still rebuilt and audited, but this can be stopped without any notice.\n- `To be defined`: not yet released or the date will be set related of another project release date (like for GeoMapFish).\n\nSee also [GitHub Documentation](https://docs.github.com/en/github/managing-security-vulnerabilities/adding-a-security-policy-to-your-repository)\n\n## Configuration\n\nThe configuration file is `.github/publish.yaml`, the schema is `https://raw.githubusercontent.com/camptocamp/tag-publish/<version>/tag_publish/schema.json`.\n\n### Dry run\n\nDry run publish: `GITHUB_REF=... tag-publish --dry-run ...`\n\n### Python package to pypi repository\n\nMinimum configuration:\n\n```yaml\npypi:\n packages:\n - {}\n```\n\nIf the file `~/.pypirc` didn't exists we will do a login using OpenId Connect (OIDC), see:\nhttps://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.\n\nBy default the package will be published only on tag, if you want to publish on stabilization branch you should add\na `versions` key with the list of versions you want to publish, that can be:\n`rebuild` (specified with --type), `tag`, `default_branch`, `stabilization_branch`, `feature_branch`, `pull_request` (for pull request merge: number)\n\nIt we have a `setup.py` file, we will be in legacy mode:\nWhen publishing, the version computed from arguments or `GITHUB_REF` is put in environment variable `VERSION`, thus you should use it in `setup.py`, example:\n\n```python\nVERSION = os.environ.get(\"VERSION\", \"1.0.0\")\n```\n\nAlso we consider that we use `poetry` with [poetry-dynamic-versioning](https://pypi.org/project/poetry-dynamic-versioning/) to manage the version, and [poetry-plugin-tweak-dependencies-version](https://pypi.org/project/poetry-plugin-tweak-dependencies-version/) to manage the dependencies versions.\n\nExample of configuration:\n\n```toml\n[tool.poetry-dynamic-versioning]\nenable = true\nvcs = \"git\"\npattern = \"^(?P<base>\\\\d+(\\\\.\\\\d+)*)\"\nformat-jinja = \"\"\"\n{%- if env.get(\"VERSION_TYPE\") == \"default_branch\" -%}\n{{serialize_pep440(bump_version(base, 1), dev=distance)}}\n{%- elif env.get(\"VERSION_TYPE\") == \"stabilization_branch\" -%}\n{{serialize_pep440(bump_version(base, 2), dev=distance)}}\n{%- elif distance == 0 -%}\n{{serialize_pep440(base)}}\n{%- else -%}\n{{serialize_pep440(bump_version(base), dev=distance)}}\n{%- endif -%}\n\"\"\"\n\n```\n\nNote that we can access to the environment variables `VERSION`,`VERSION_TYPE`.\n\nThen by default:\n\n- Tag with `1.2.3` => release `1.2.3`\n- Commit on feature branch just do a validation\n- Commit on `master` branch after the tag 1.3.0 => release `1.4.0.dev1`\n- Commit on `1.3` branch after the tag 1.3.0 => release `1.3.1.dev1`\n\n#### Authentication\n\nIf the file `~/.pypirc` exists we consider that we ar already logged in also\nwe will do the login with the `pypi` server with OpenID Connect (OIDC).\n\nThe OIDC login is recommended because it didn't needs any additional secrets,\nbut it need some configuration on pypi in the package,\nsee the [GitHub Documentation](https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi#adding-the-identity-provider-to-pypi).\n\nThe required permissions is `id-token: write`.\n\n#### Integration if the package directly in a Docker image\n\nTo make it working in the `Dockerfile` you should have in the `poetry` stage:\n\n```Dockerfile\nENV POETRY_DYNAMIC_VERSIONING_BYPASS=dev\nRUN poetry export --extras=checks --extras=publish --output=requirements.txt \\\n && poetry export --with=dev --output=requirements-dev.txt\n```\n\nAnd in the `run` stage\n\n```Dockerfile\nARG VERSION=dev\nRUN --mount=type=cache,target=/root/.cache \\\n POETRY_DYNAMIC_VERSIONING_BYPASS=${VERSION} python3 -m pip install --disable-pip-version-check --no-deps --editable=.\n```\n\nAnd in the `Makefile`:\n\n```Makefile\nVERSION = $(strip $(shell poetry version --short))\n\n.PHONY: build\nbuild: ## Build the Docker images\n docker build --build-arg=VERSION=$(VERSION) --tag=$(GITHUB_REPOSITORY) .\n```\n\n### Docker image to registry\n\nThe minimal config is like this:\n\n```yaml\ndocker:\n images:\n - name: camptocamp/tag-publish\n```\n\nIf you want to use the GitHub token to be logged in on ghcr you should set `auto_login` to `True`, the\nrequires the permissions are `packages: write`.\n\nWith that the image initially named `camptocamp/tag-publish:latest` will be published on GitHub GHCR and on Docker hub.\n\nThe full config is like this:\n\n```yaml\ndocker:\n github_oidc_login: True\n latest: True\n images:\n - # The base name of the image we want to publish\n name:\n repository:\n <internal_name>:\n # The fqdn name of the server if not Docker hub\n server:\n # List of kinds of versions you want to publish, that can be: rebuild (specified using --type),\n # tag, stabilization_branch, feature_branch, pull_request (for pull request merge: number)\n version:\n # List of tags we want to publish interpreted with `format(version=version)`\n # e.g. if you use `{version}-lite` when you publish the version `1.2.3` the source tag\n # (that should be built by the application build) is `latest-lite`, and it will be published\n # with the tag `1.2.3-lite`.\n tags:\n # If your images are published by different jobs you can separate them in different groups\n # and publish them with `tag-publish --group=<group>`\n group:\n```\n\nBy default, the last line of the `SECURITY.md` file will be published (`docker`) with the tag\n`latest`. Set `latest` to `False` to disable it.\n\n#### Use Renovate to trigger a new build instead of the legacy rebuild\n\nIf the `ci/dpkg-versions.yaml` or `.github/dpkg-versions.yaml` file is present, the package list will be updated on publishing.\n\nThe versions will be updated by [GHCI](https://github.com/camptocamp/github-app-geo-project/) application.\n\n### Node package to npm repository\n\nMinimum configuration:\n\n```yaml\nnode:\n packages:\n - {}\n```\n\nIf the repository server is `npm.pkg.github.com` we will do a login using `GITHUB_TOKEN`.\n\nTo publish on `npm.pkg.github.com` you requires the permissions are `packages: write`, and the `id-token: write` for the provenance.\n\nBy default the package will be published only on tag.\n\n### HELM\n\nThe minimal config is like this:\n\n```yaml\nhelm:\n packages:\n - {}\n```\n\nThis will publish the `helm` charts in the current folder using [chart releaser](https://github.com/helm/chart-releaser).\n\nThe artifacts will be attached to a GitHub release, and the `index.yaml` file will be updated in the `gh-pages` branch.\n\nThe required permission is `contents: write`.\n\nCreate the required `gh-pages` branch:\n\n```bash\ngit checkout --orphan gh-pages\ngit reset --hard\ngit commit --allow-empty -m \"Initialize gh-pages branch\"\ngit push origin gh-pages\n```\n\n## Dispatch\n\nThe minimal config is like this:\n\n```yaml\ndispatch:\n - {}\n```\n\nThe required permission is `contents: write`.\n\nThis will create a repository dispatch of type `published` on own repository with the `content` e.g.:\n\n```json\n{\n \"version\": \"1.2.3\",\n \"version_type\": \"tag\",\n \"repository\": \"camptocamp/tag-publish\",\n \"items\": [\n {\n \"type\": \"docker\",\n \"image\": \"camptocamp/tag-publish\",\n \"repository\": \"ghcr.io\",\n \"tag\": \"1.2.3\"\n },\n {\n \"type\": \"pypi\",\n \"path\": \".\"\n },\n {\n \"type\": \"helm\",\n \"path\": \".\"\n }\n ]\n}\n```\n\n## Contributing\n\nInstall the pre-commit hooks:\n\n```bash\npip install pre-commit\npre-commit install --allow-missing-config\n```\n\n",
"bugtrack_url": null,
"license": "FreeBSD",
"summary": "Tools used to publish Python packages, Docker images and Helm charts for GitHub tag and branch",
"version": "0.13.3",
"project_urls": {
"Homepage": "https://github.com/camptocamp/tag-publish",
"Repository": "https://github.com/camptocamp/tag-publish"
},
"split_keywords": [
"ci"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2f9692ca8f7fc380c3b3de421675a6cce52de4863fe19d2d50e72db8020fbd59",
"md5": "bd75788e8ac0e3bf67cb596e4c462696",
"sha256": "02d041174d3bb0fd2e9f6f65f3efed391f8f73a12834a6901e2fdfa16149c985"
},
"downloads": -1,
"filename": "tag_publish-0.13.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bd75788e8ac0e3bf67cb596e4c462696",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 27302,
"upload_time": "2024-11-28T14:30:51",
"upload_time_iso_8601": "2024-11-28T14:30:51.603975Z",
"url": "https://files.pythonhosted.org/packages/2f/96/92ca8f7fc380c3b3de421675a6cce52de4863fe19d2d50e72db8020fbd59/tag_publish-0.13.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "aacf9672e5418c9a935cb95dc506c17eb75dca01be5d9d6b49d3da507a17abb4",
"md5": "f17c4a33032a158f3ce0e2c2ebc7de41",
"sha256": "9fda0869a46d0faef65f0b13e8fe4e3ab69d91255dcf83c9fa8324bc8bc588f8"
},
"downloads": -1,
"filename": "tag_publish-0.13.3.tar.gz",
"has_sig": false,
"md5_digest": "f17c4a33032a158f3ce0e2c2ebc7de41",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 27017,
"upload_time": "2024-11-28T14:30:52",
"upload_time_iso_8601": "2024-11-28T14:30:52.780969Z",
"url": "https://files.pythonhosted.org/packages/aa/cf/9672e5418c9a935cb95dc506c17eb75dca01be5d9d6b49d3da507a17abb4/tag_publish-0.13.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-28 14:30:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "camptocamp",
"github_project": "tag-publish",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "poetry",
"specs": [
[
"==",
"1.8.4"
]
]
},
{
"name": "poetry-plugin-export",
"specs": [
[
"==",
"1.8.0"
]
]
},
{
"name": "poetry-dynamic-versioning",
"specs": [
[
"==",
"1.4.1"
]
]
},
{
"name": "poetry-plugin-tweak-dependencies-version",
"specs": [
[
"==",
"1.5.2"
]
]
},
{
"name": "pre-commit",
"specs": [
[
"==",
"4.0.1"
]
]
}
],
"lcname": "tag-publish"
}