<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mel Cadano</title>
    <description>The latest articles on DEV Community by Mel Cadano (@melcdn).</description>
    <link>https://dev.to/melcdn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3979145%2Fa4b8af47-0a31-4a71-96af-448e4c136534.png</url>
      <title>DEV Community: Mel Cadano</title>
      <link>https://dev.to/melcdn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/melcdn"/>
    <language>en</language>
    <item>
      <title>Automate Python Package Releases: Semantic Release, AWS CodeArtifact, Poetry and Bitbucket Pipelines</title>
      <dc:creator>Mel Cadano</dc:creator>
      <pubDate>Sun, 14 Jun 2026 15:15:32 +0000</pubDate>
      <link>https://dev.to/melcdn/automate-python-package-releases-semantic-release-aws-codeartifact-poetry-and-bitbucket-pipelines-95m</link>
      <guid>https://dev.to/melcdn/automate-python-package-releases-semantic-release-aws-codeartifact-poetry-and-bitbucket-pipelines-95m</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Maintaining a private Python package shouldn't mean manually bumping versions, tagging commits, and pushing releases every time you ship a change. For a while, that's exactly what I was doing — editing pyproject.toml, running git tag, pushing, and hoping I didn't fat-finger the version number.&lt;/p&gt;

&lt;p&gt;In this article, we'll wire up a fully automated release pipeline for a &lt;strong&gt;private Python package&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Poetry&lt;/strong&gt; — dependency management and packaging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python Semantic Release&lt;/strong&gt; — automatic version bumps from commit messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitbucket Pipelines&lt;/strong&gt; — CI/CD runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CodeArtifact&lt;/strong&gt; — private package registry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, every merge to main will automatically bump the version, generate a changelog, tag the commit, and publish to CodeArtifact — based purely on your commit messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Bitbucket&lt;/strong&gt; account (workspace + repo creation rights)&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;AWS&lt;/strong&gt; account (CodeArtifact access)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python 3.11+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reference repository&lt;/strong&gt;&lt;br&gt;
The complete sample code and starter files used throughout this article are available on this &lt;strong&gt;GitHub repository&lt;/strong&gt;: [&lt;a href="https://github.com/mel-cdn/playground-hub-python-shared" rel="noopener noreferrer"&gt;https://github.com/mel-cdn/playground-hub-python-shared&lt;/a&gt;].&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 — Initialize the Private Python Library Repository
&lt;/h2&gt;

&lt;p&gt;We'll commit an initial v0.0.0 baseline so semantic-release has a starting point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Already have an existing package with a version? Skip ahead and manually tag your current version, then push the tag to origin. See &lt;strong&gt;[Step 1.7]&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  1.1 Create an empty Bitbucket repository
&lt;/h3&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/workspace/create/repository" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/workspace/create/repository&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt8ji1tedkiavpfi0axe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt8ji1tedkiavpfi0axe.png" alt="Create an empty repository" width="613" height="859"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1.2 Clone it locally
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone git@bitbucket.org:[your-workspace]/phub-python-shared.git
Cloning into &lt;span class="s1"&gt;'phub-python-shared'&lt;/span&gt;...
warning: You appear to have cloned an empty repository.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  1.3 Add the initial files
&lt;/h3&gt;

&lt;p&gt;You can check out the &lt;code&gt;initial-files&lt;/code&gt; branch of the &lt;strong&gt;Reference Repository&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;phub-python-shared
├─ phub_python_shared/   # The package source
│ ├── __init__
│ ├── unique_id.py
├─ tests/                # Unit tests files
│ ├── __init__
│ ├── test_unique_id.py
├─ .gitignore
├─ poetry.lock           # The snapshot of the package versions
├─ pyproject.toml        # The package build configuration
├─ README.md             # The manual
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.4 Configure &lt;code&gt;pyproject.toml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let's walk through the relevant sections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project metadata&lt;/strong&gt; — note the initial version is &lt;code&gt;0.0.0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Project Metadata&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"phub-python-shared"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Python Shared Utilities for Playground Hub"&lt;/span&gt;
&lt;span class="py"&gt;readme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"README.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;content-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"text/markdown"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Mel Cadano"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mel.cdn@outlook.ph"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Build system &amp;amp; dependencies&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Build system&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;["poetry-core&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;"]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"poetry.core.masonry.api"&lt;/span&gt;

&lt;span class="nn"&gt;[[tool.poetry.source]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PyPI"&lt;/span&gt;
&lt;span class="py"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"primary"&lt;/span&gt;

&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Project Dependencies&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mf"&gt;3.13&lt;/span&gt;&lt;span class="py"&gt;"
ulid = "&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="py"&gt;"

# -----------------------------
# Dev Dependencies
# -----------------------------
[tool.poetry.group.dev.dependencies]
pytest = "&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;9.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="py"&gt;"
python-semantic-release = "&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.5&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Semantic Release configuration&lt;/strong&gt; — this tells &lt;code&gt;semantic-release&lt;/code&gt; to target &lt;strong&gt;Bitbucket&lt;/strong&gt;, write a &lt;code&gt;CHANGELOG.md&lt;/code&gt;, and keep the version in &lt;code&gt;pyproject.toml&lt;/code&gt; in sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Semantic Release Configuration&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="nn"&gt;[tool.semantic_release]&lt;/span&gt;
&lt;span class="c"&gt;# Commit Configurations&lt;/span&gt;
&lt;span class="py"&gt;vcs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bitbucket"&lt;/span&gt;
&lt;span class="py"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt;
&lt;span class="py"&gt;allow_zero_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;changelog_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CHANGELOG.md"&lt;/span&gt;
&lt;span class="py"&gt;commit_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"✨ Bump to version {version} [skip ci]"&lt;/span&gt;
&lt;span class="py"&gt;commit_parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"conventional"&lt;/span&gt;
&lt;span class="py"&gt;tag_format&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v{version}"&lt;/span&gt;

&lt;span class="c"&gt;# For the CHANGELOG.MD builder&lt;/span&gt;
&lt;span class="py"&gt;repository_author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mello-world"&lt;/span&gt;
&lt;span class="py"&gt;repository_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"phub-python-shared"&lt;/span&gt;

&lt;span class="py"&gt;version_toml&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"pyproject.toml:project.version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.semantic_release.remote]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"origin"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bitbucket"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Commit-message → version bump mapping&lt;/strong&gt;. Semantic-release reads your commit messages to decide what the next version will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# For Version Tagging&lt;/span&gt;
&lt;span class="nn"&gt;[tool.semantic_release.commit_parser_options]&lt;/span&gt;
&lt;span class="py"&gt;patch_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"fix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"chore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"docs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"refactor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;minor_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"feat"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;major_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"feat!"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commit prefix&lt;/th&gt;
&lt;th&gt;Bump type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;fix:&lt;/code&gt;, &lt;code&gt;chore:&lt;/code&gt;, &lt;code&gt;docs:&lt;/code&gt;, &lt;code&gt;style:&lt;/code&gt;, &lt;code&gt;refactor:&lt;/code&gt;, &lt;code&gt;test:&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Patch (0.0.X)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fix: handle empty input&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feat:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minor (0.X.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feat: add ULID generator&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feat!:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Major (X.0.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feat!: drop Python 3.10 support&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  1.5 Now let's commit these as starter files.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add initial files"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.6 Install the package and run a local version bump
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;poetry
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;poetry &lt;span class="nb"&gt;env &lt;/span&gt;activate&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-root&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;semantic-release version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;01:36:15] WARNING  Token value is missing!                                                                                                                                                                                                    config.py:782
0.0.0
The next version is: 0.0.0! 🚀
WARNING:root:[Errno 2] No such file or directory: &lt;span class="s1"&gt;'.../phub-python-shared/CHANGELOG.md'&lt;/span&gt;
No build &lt;span class="nb"&gt;command &lt;/span&gt;specified, skipping
::ERROR:: Requested to use token but no token set.
Run semantic-release &lt;span class="k"&gt;in &lt;/span&gt;very verbose mode &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-vv&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; to see the full traceback.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ The "Token value is missing" error is expected locally — pushing to the remote requires a token, which we'll configure later in Bitbucket Pipelines. The local tag and changelog are still created.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since there's no &lt;code&gt;feat/fix&lt;/code&gt; commit yet, the version doesn't bump — but because the &lt;code&gt;v0.0.0&lt;/code&gt; tag didn't exist, semantic-release creates one matching the current version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git tag &lt;span class="nt"&gt;--list&lt;/span&gt;
v0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking your git log, you should also see a &lt;strong&gt;Bump version&lt;/strong&gt; commit authored by &lt;code&gt;semantic-release&lt;/code&gt;, along with an initial &lt;code&gt;CHANGELOG.md&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7to0nmrgbq5rk2vw7dx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7to0nmrgbq5rk2vw7dx.png" alt="First version bump" width="677" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CHANGELOG.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# CHANGELOG

&amp;lt;!-- version list --&amp;gt;

## v0.0.0 (2026-06-14)

- Initial Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.7 Push &lt;code&gt;main&lt;/code&gt; and the &lt;code&gt;v0.0.0&lt;/code&gt; tag to origin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin main
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin tag v0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Confirm in Bitbucket that the &lt;code&gt;main&lt;/code&gt; branch and the &lt;code&gt;v0.0.0&lt;/code&gt; tag are both present.&lt;br&gt;
[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/commits/branch/main" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/commits/branch/main&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqbgah6gthyr3xcvxqx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqbgah6gthyr3xcvxqx1.png" alt="Repository main branch" width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/commits/tag/v0.0.0" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/commits/tag/v0.0.0&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4grmqg2ji0hyrd8ejt0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi4grmqg2ji0hyrd8ejt0.png" alt="Repository tag v0.0.0" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2 — Automate the Bump in Bitbucket Pipelines
&lt;/h2&gt;

&lt;p&gt;Now that local bumping works, let's move the tagging into Bitbucket Pipelines so every merge to main is versioned automatically.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.1 Create a Bitbucket access token (bot)
&lt;/h3&gt;

&lt;p&gt;Semantic-release needs to &lt;strong&gt;push commits and tags back to main&lt;/strong&gt;, which the default &lt;code&gt;$BITBUCKET_*&lt;/code&gt; pipeline credentials can't do. Create a dedicated bot token:&lt;br&gt;
[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/admin/access-tokens" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/admin/access-tokens&lt;/a&gt;]&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Repository settings → Security → Access tokens → Create access Token&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name it something like &lt;code&gt;Version Crafter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Grant the following scopes: &lt;code&gt;repository:write&lt;/code&gt;.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31euq25ksguhz5zzpplo.png" alt="Create access token" width="600" height="701"&gt;
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;generated token&lt;/strong&gt; and &lt;strong&gt;email&lt;/strong&gt;.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2995zs9ejipf7fdli5qo.png" alt="Created access token configuration" width="602" height="841"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2.2 Update the &lt;code&gt;pyproject.toml&lt;/code&gt;.
&lt;/h3&gt;

&lt;p&gt;Wire up the bot identity so &lt;strong&gt;semantic-release&lt;/strong&gt; can sign commits and push tags on your behalf. Use the &lt;strong&gt;access token name&lt;/strong&gt; and &lt;strong&gt;email&lt;/strong&gt; from the previous step as the &lt;code&gt;commit_author&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;commit_author&lt;/code&gt; line to your &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Semantic Release Configuration&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------&lt;/span&gt;
&lt;span class="nn"&gt;[tool.semantic_release]&lt;/span&gt;
&lt;span class="c"&gt;# Commit Configurations&lt;/span&gt;
&lt;span class="py"&gt;vcs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bitbucket"&lt;/span&gt;
&lt;span class="py"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt;
&lt;span class="py"&gt;allow_zero_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;changelog_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CHANGELOG.md"&lt;/span&gt;
&lt;span class="py"&gt;commit_author&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Version Crafter &amp;lt;y1qvnblznuaapeboc86b0w62qhkuj0@bots.bitbucket.org&amp;gt;"&lt;/span&gt;
&lt;span class="py"&gt;commit_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"✨ Bump to version {version} [skip ci]"&lt;/span&gt;
&lt;span class="py"&gt;commit_parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"conventional"&lt;/span&gt;
&lt;span class="py"&gt;tag_format&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v{version}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Update the Repository Variables for the Pipeline
&lt;/h3&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/admin/pipelines/repository-variables" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/admin/pipelines/repository-variables&lt;/a&gt;]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🛠️ You need to &lt;strong&gt;enable pipelines&lt;/strong&gt; first to be able to add &lt;strong&gt;repository variables&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;strong&gt;Repository settings → Repository variables&lt;/strong&gt;, add:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Secured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BITBUCKET_BOT_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(the access token)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftu824ijbtaygz9pdhrvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftu824ijbtaygz9pdhrvv.png" alt="New variable added" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 Add the initial &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; at the repo root with two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unit Tests&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bump Version&lt;/strong&gt; - using &lt;code&gt;BITBUCKET_BOT_TOKEN&lt;/code&gt; to push the next tag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The pipeline has two steps. unit tests and bump version. bump version will use &lt;code&gt;BITBUCKET_BOT_TOKEN&lt;/code&gt; so it can push the next tag&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:3.12&lt;/span&gt;

&lt;span class="na"&gt;definitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;lint-and-tests&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Unit Tests&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-root&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry run python -m pytest&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;bump-version&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bump Version&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install python-semantic-release==10.5.3&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export BITBUCKET_TOKEN=$BITBUCKET_BOT_TOKEN&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git remote set-url origin "https://x-token-auth:${BITBUCKET_TOKEN}@bitbucket.org/${BITBUCKET_REPO_FULL_NAME}.git"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PYTHONPATH="${PWD}" semantic-release version&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Runs when code hits the main branch&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*lint-and-tests&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*bump-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.5 Commit, push, and validate the pipeline
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;pyproject.toml&lt;/code&gt; and &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; in place, commit both files and push to &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
We'll use a &lt;code&gt;chore&lt;/code&gt; commit type — this is an intentional change that &lt;code&gt;semantic-release&lt;/code&gt; will detect and bump to the first real version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add pyproject.toml bitbucket-pipelines.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"chore: Configure package build and pipeline configuration"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head over to &lt;strong&gt;Pipelines&lt;/strong&gt; in your Bitbucket repository to watch the first run:&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/pipelines" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/pipelines&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline Overview&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro2a1vjmc2jpvskrhhb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro2a1vjmc2jpvskrhhb4.png" alt="First pipeline" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline Steps&lt;/strong&gt;&lt;br&gt;
Verify that the &lt;strong&gt;bump version&lt;/strong&gt; step completes successfully and bumps the version to &lt;code&gt;v0.0.1&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5eml1mbuvkqyc6pir94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5eml1mbuvkqyc6pir94.png" alt="Pipeline steps" width="799" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, confirm in Bitbucket that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The &lt;code&gt;main&lt;/code&gt; branch now contains the automated bump commit.&lt;/li&gt;
&lt;li&gt;✅ The new &lt;code&gt;v0.0.1&lt;/code&gt; tag is present.&lt;/li&gt;
&lt;li&gt;✅ The bot account - &lt;code&gt;Version Crafter&lt;/code&gt; is the author of the commit instead of the default &lt;code&gt;semantic-release&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlswsk2edpp23b1mqhhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlswsk2edpp23b1mqhhb.png" alt="Repository main branch" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h3cx1hjm14ir7dw6ex7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h3cx1hjm14ir7dw6ex7.png" alt="Repository tag v0.0.1" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3 — Automate the Push to AWS CodeArtifact
&lt;/h2&gt;

&lt;p&gt;With versioning automated, the last piece is &lt;strong&gt;publishing the built wheel to a private registry&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.1 Create the CodeArtifact domain &amp;amp; repository
&lt;/h3&gt;

&lt;p&gt;In the AWS Console, go to &lt;strong&gt;CodeArtifact → Domains → Create domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfq8etvepf26mmelay3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfq8etvepf26mmelay3f.png" alt="Create a CodeArtifact domain" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then create a repository inside that domain. You can leave the upstream as &lt;code&gt;pypi-store&lt;/code&gt; so missing public dependencies are proxied transparently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2m43o96ozo5ijd3g3ci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2m43o96ozo5ijd3g3ci.png" alt="Create a CodeArtifact repository" width="799" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq4r79lf41pmtla7km75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq4r79lf41pmtla7km75.png" alt="Created an empty repository" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3.2 Create a deployer IAM user
&lt;/h3&gt;

&lt;p&gt;The pipeline needs AWS credentials to push to CodeArtifact. Create a dedicated IAM user for it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 For simplicity, we'll use an IAM user with access keys. If you'd rather use short-lived credentials via OIDC (recommended for production), follow this guide instead: &lt;a href="https://dev.to/melcdn/bitbucket-pipelines-for-aws-using-openid-connect-oidc-3oi5"&gt;Bitbucket → AWS OIDC setup&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feegyjp133kwetxua28io.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feegyjp133kwetxua28io.png" alt="Creating the IAM user deployer" width="799" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔐 Attached &lt;strong&gt;AWS-managed policy&lt;/strong&gt; is &lt;code&gt;PowerUserAccess&lt;/code&gt; for a quick setup. For &lt;strong&gt;production&lt;/strong&gt;, you can use below in-line policy to restrict to the &lt;strong&gt;least privilege required&lt;/strong&gt; (recommended).&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact:GetAuthorizationToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact:GetRepositoryEndpoint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact:ReadFromRepository"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact:PublishPackageVersion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact:PutPackageMetadata"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:GetServiceBearerToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sts:AWSServiceName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"codeartifact.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Generate an access key
&lt;/h3&gt;

&lt;p&gt;After creating the IAM user, On the user's &lt;strong&gt;Security credentials&lt;/strong&gt; tab, click &lt;strong&gt;Create access key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbhf2gsfd8421z29jsr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbhf2gsfd8421z29jsr5.png" alt="Create IAM user access key" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy both the &lt;strong&gt;Access key ID&lt;/strong&gt; and &lt;strong&gt;Secret access key&lt;/strong&gt; — the secret is shown only once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc9bcmfal6sso8n6ye0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc9bcmfal6sso8n6ye0b.png" alt="Auto-generated access keys" width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Update the repository variables for the pipeline
&lt;/h3&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/admin/pipelines/repository-variables" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/admin/pipelines/repository-variables&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Add the &lt;strong&gt;AWS&lt;/strong&gt; keys and values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Secured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_ACCOUNT_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(13 digit AWS account number)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_DEFAULT_REGION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(AWS region)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(IAM user access key)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(IAM user secret access key)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuk35shwuw8cmxs3coy0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuk35shwuw8cmxs3coy0k.png" alt="Updated repository variables with AWS configurations" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 Add the Publish step to pipeline configuration
&lt;/h3&gt;

&lt;p&gt;Pull the latest commits and tags from your remote origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;publish&lt;/code&gt; step to &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; and update with the following workflow rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lint-and-test&lt;/code&gt;: Runs on all feature branches, excluding &lt;code&gt;main&lt;/code&gt;, to validate code before merging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bump-version&lt;/code&gt;: Runs strictly on &lt;code&gt;main&lt;/code&gt; branch to handle automated versioning and tagging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publish&lt;/code&gt;: Triggers only when a new tag is pushed, building and uploading the package to &lt;strong&gt;AWS CodeArtifact&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Updated &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:3.12&lt;/span&gt;

&lt;span class="na"&gt;definitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;lint-and-tests&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Unit Tests&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-root&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry run python -m pytest&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;bump-version&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bump Version&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install python-semantic-release==10.5.3&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export BITBUCKET_TOKEN=$BITBUCKET_CI_TOKEN&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git remote set-url origin "https://x-token-auth:${BITBUCKET_TOKEN}@bitbucket.org/${BITBUCKET_REPO_FULL_NAME}.git"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PYTHONPATH="${PWD}" semantic-release version&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;publish&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to CodeArtifact&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Install dependencies&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get update -qq &amp;amp;&amp;amp; apt-get install -y -qq jq&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install awscli==1.45.25 poetry==2.4.1&lt;/span&gt;

          &lt;span class="c1"&gt;# Preparing CodeArtifact repository&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;AUTH_TOKEN=$(&lt;/span&gt;
              &lt;span class="s"&gt;aws codeartifact get-authorization-token \&lt;/span&gt;
              &lt;span class="s"&gt;--domain "playground-hub" \&lt;/span&gt;
              &lt;span class="s"&gt;--domain-owner $AWS_ACCOUNT_ID \&lt;/span&gt;
              &lt;span class="s"&gt;--region $AWS_DEFAULT_REGION \&lt;/span&gt;
              &lt;span class="s"&gt;--query 'authorizationToken' \&lt;/span&gt;
              &lt;span class="s"&gt;--output text&lt;/span&gt;
            &lt;span class="s"&gt;)            &lt;/span&gt;
            &lt;span class="s"&gt;REPO_URL=$(&lt;/span&gt;
              &lt;span class="s"&gt;aws codeartifact get-repository-endpoint \&lt;/span&gt;
              &lt;span class="s"&gt;--domain "playground-hub" \&lt;/span&gt;
              &lt;span class="s"&gt;--domain-owner $AWS_ACCOUNT_ID \&lt;/span&gt;
              &lt;span class="s"&gt;--region $AWS_DEFAULT_REGION \&lt;/span&gt;
              &lt;span class="s"&gt;--repository "phub-python-shared" \&lt;/span&gt;
              &lt;span class="s"&gt;--format pypi \&lt;/span&gt;
              &lt;span class="s"&gt;--query 'repositoryEndpoint' \&lt;/span&gt;
              &lt;span class="s"&gt;--output text&lt;/span&gt;
            &lt;span class="s"&gt;)&lt;/span&gt;

          &lt;span class="c1"&gt;# Building and push package&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;poetry build&lt;/span&gt;
            &lt;span class="s"&gt;poetry config repositories.phub-python-shared "$REPO_URL"&lt;/span&gt;
            &lt;span class="s"&gt;poetry config http-basic.phub-python-shared aws "$AUTH_TOKEN"&lt;/span&gt;
            &lt;span class="s"&gt;poetry publish -r phub-python-shared&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# A. Runs on every branch EXCEPT main&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*lint-and-tests&lt;/span&gt;

  &lt;span class="c1"&gt;# B. Runs ONLY when code hits the main branch&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*bump-version&lt;/span&gt;

  &lt;span class="c1"&gt;# C. Runs ONLY when there's a new tag with vX.X.X format&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*.*.*'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*publish&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.6 Commit and push to Publish
&lt;/h3&gt;

&lt;p&gt;We'll intentionally use &lt;code&gt;feat&lt;/code&gt; commit type to trigger the bumping of the feature identifier&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add bitbucket-pipelines.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: Add publish AWS CodeArtifact publish step on tags"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head over again to &lt;strong&gt;Pipelines&lt;/strong&gt; in your Bitbucket repository:&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://bitbucket.org/%5Byour-workspace%5D/phub-python-shared/pipelines" rel="noopener noreferrer"&gt;https://bitbucket.org/[your-workspace]/phub-python-shared/pipelines&lt;/a&gt;]&lt;/p&gt;

&lt;h3&gt;
  
  
  3.7 Validate the publish
&lt;/h3&gt;

&lt;p&gt;Once the &lt;code&gt;main&lt;/code&gt; branch pipeline completes, the &lt;strong&gt;automated version bump&lt;/strong&gt; kicks off the tag trigger.&lt;br&gt;
The pipeline will automatically run the &lt;strong&gt;publish&lt;/strong&gt; step immediately after the version tag (&lt;code&gt;v0.1.0&lt;/code&gt;) is generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F240ahthzk5bccswkyaub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F240ahthzk5bccswkyaub.png" alt="Publish after version bump" width="799" height="297"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqs3ux22k3bd3n84ngwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqs3ux22k3bd3n84ngwk.png" alt="Publish successful" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, log into your &lt;strong&gt;AWS Console&lt;/strong&gt; and navigate to &lt;strong&gt;AWS CodeArtifact&lt;/strong&gt; to verify that your new package version is available in the repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zhkwe9xiykx4031njeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zhkwe9xiykx4031njeb.png" alt="AWS CodeArtifact" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that's it! You have successfully configured an automated Python package versioning and publishing pipeline using &lt;strong&gt;Bitbucket Pipelines&lt;/strong&gt; and &lt;strong&gt;AWS CodeArtifact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a future article, I will cover how to easily pull down and consume this package as a standard library inside your Python applications.&lt;/p&gt;

&lt;p&gt;Salamat po 🇵🇭&lt;/p&gt;

</description>
      <category>codeartifact</category>
      <category>python</category>
      <category>bitbucket</category>
      <category>poetry</category>
    </item>
    <item>
      <title>Bitbucket Pipelines for AWS using OpenID Connect (OIDC)</title>
      <dc:creator>Mel Cadano</dc:creator>
      <pubDate>Sun, 14 Jun 2026 15:13:32 +0000</pubDate>
      <link>https://dev.to/melcdn/bitbucket-pipelines-for-aws-using-openid-connect-oidc-3oi5</link>
      <guid>https://dev.to/melcdn/bitbucket-pipelines-for-aws-using-openid-connect-oidc-3oi5</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Storing permanent AWS Access Keys (&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;) in your CI/CD variables introduces significant security risks. &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; solves this by establishing a direct trust relationship between Bitbucket and AWS.&lt;/p&gt;

&lt;p&gt;To keep things clean, we will walk through creating a fresh Bitbucket repository to isolate and test this OIDC connection easily. Once this foundation is built, you can mirror the exact same pipeline configuration to securely handshake with AWS across any of your other repositories!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bitbucket Account&lt;/strong&gt;: Permissions to create a workspace and a repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Account&lt;/strong&gt;: Administrative or IAM management access to create Identity Providers and Roles.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1 — Initialize a Bitbucket Repository
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.1 -&lt;/strong&gt; Create a new repository for testing the OIDC connection.&lt;br&gt;
For this guide, I will use: &lt;code&gt;phub-oidc-check&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2 -&lt;/strong&gt; Head over to OIDC information of your repository:&lt;br&gt;
[&lt;a href="https://bitbucket.org/%5Bworkspace-name%5D/phub-oidc-check/admin/pipelines/openid-connect" rel="noopener noreferrer"&gt;https://bitbucket.org/[workspace-name]/phub-oidc-check/admin/pipelines/openid-connect&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.3 -&lt;/strong&gt; Secure the &lt;code&gt;Identity provider URL&lt;/code&gt; and &lt;code&gt;Audience&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kjprw1mq3oi6sixbapz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kjprw1mq3oi6sixbapz.png" alt="OpenID Connect" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2 — Configure the Identity Provider in AWS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2.1 -&lt;/strong&gt; Navigate to the &lt;strong&gt;IAM Console &amp;gt; Identity Providers &amp;gt; Add Provider&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvi4af4933ianod8fxrmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvi4af4933ianod8fxrmd.png" alt="Add Identity Provider" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3 — Create the IAM Role for Bitbucket
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1 -&lt;/strong&gt; In the &lt;strong&gt;IAM Console&lt;/strong&gt;, go to &lt;strong&gt;Roles &amp;gt; Create Role&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;3.2 -&lt;/strong&gt; Select Custom Trust Policy and paste the following JSON setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowBitbucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/api.bitbucket.org/2.0/workspaces/&amp;lt;WORKSPACE_NAME&amp;gt;/pipelines-config/identity/oidc"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"api.bitbucket.org/2.0/workspaces/&amp;lt;WORKSPACE_NAME&amp;gt;/pipelines-config/identity/oidc:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ari:cloud:bitbucket::workspace/&amp;lt;WORKSPACE_UUID&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.3 -&lt;/strong&gt; Attach &lt;strong&gt;AWS-managed policy&lt;/strong&gt; is &lt;code&gt;PowerUserAccess&lt;/code&gt; for a quick setup. For &lt;strong&gt;production&lt;/strong&gt;, you can use below in-line policy to restrict to the &lt;strong&gt;least privilege required&lt;/strong&gt; (recommended).&lt;br&gt;
&lt;strong&gt;3.4 -&lt;/strong&gt; Name the role (e.g., &lt;code&gt;bitbucket-pipeline-oidc-role&lt;/code&gt;) and copy its ARN.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4 — Enable OIDC in Bitbucket Pipelines
&lt;/h2&gt;

&lt;p&gt;Enable Bitbucket to generate the OIDC token (Repository Variables config) and update your pipeline script.&lt;br&gt;
[&lt;a href="https://bitbucket.org/%5Bworkspace-name%5D/phub-oidc-check/admin/pipelines/repository-variables" rel="noopener noreferrer"&gt;https://bitbucket.org/[workspace-name]/phub-oidc-check/admin/pipelines/repository-variables&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.1 -&lt;/strong&gt; In your Bitbucket Repository settings, go to Pipelines &amp;gt; Repository variables and add the &lt;code&gt;AWS_OIDC_ROLE_ARN&lt;/code&gt; and &lt;code&gt;AWS_DEFAULT_REGION&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🛠️ You need to &lt;strong&gt;enable pipelines&lt;/strong&gt; first to be able to add &lt;strong&gt;repository variables&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fie89k3v3w2lxqr82cm8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fie89k3v3w2lxqr82cm8x.png" alt="Repository variables" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 -&lt;/strong&gt; Add the &lt;strong&gt;bitbucket-pipelines.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/aws-cli:latest&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;test-aws-oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test AWS OIDC Connection&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Very Important&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yum install -y jq&lt;/span&gt;

            &lt;span class="c1"&gt;# Handshake with AWS IAM using OIDC token&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;ASSUME_ROLE_JSON=$(aws sts assume-role-with-web-identity \&lt;/span&gt;
                &lt;span class="s"&gt;--role-arn "$AWS_OIDC_ROLE_ARN" \&lt;/span&gt;
                &lt;span class="s"&gt;--role-session-name "BitbucketTestSession" \&lt;/span&gt;
                &lt;span class="s"&gt;--web-identity-token "$BITBUCKET_STEP_OIDC_TOKEN" \&lt;/span&gt;
                &lt;span class="s"&gt;--output json)              &lt;/span&gt;

            &lt;span class="c1"&gt;# Activating session&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;export AWS_ACCESS_KEY_ID=$(echo "$ASSUME_ROLE_JSON" | jq -r '.Credentials.AccessKeyId')&lt;/span&gt;
              &lt;span class="s"&gt;export AWS_SECRET_ACCESS_KEY=$(echo "$ASSUME_ROLE_JSON" | jq -r '.Credentials.SecretAccessKey')&lt;/span&gt;
              &lt;span class="s"&gt;export AWS_SESSION_TOKEN=$(echo "$ASSUME_ROLE_JSON" | jq -r '.Credentials.SessionToken')&lt;/span&gt;

            &lt;span class="c1"&gt;# Verify the activated role&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws sts get-caller-identity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.3 -&lt;/strong&gt; Commit, push and validate the pipeline.&lt;br&gt;
Push the &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; to repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add bitbucket-pipelines.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"ci: Add pipeline configuration"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head over to pipelines and trigger the custom pipeline manually.&lt;br&gt;
[&lt;a href="https://bitbucket.org/%5Bworkspace-name%5D/phub-oidc-check/pipelines" rel="noopener noreferrer"&gt;https://bitbucket.org/[workspace-name]/phub-oidc-check/pipelines&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81gy8f2ztdxfwgyxrnsl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81gy8f2ztdxfwgyxrnsl.png" alt="Run custom pipeline" width="600" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbphskecvsddpl0wxniy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbphskecvsddpl0wxniy.png" alt="Running pipeline" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, check the &lt;code&gt;aws sts get-caller-identity&lt;/code&gt; sub step you should see the familiar &lt;strong&gt;OIDC role ARN&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2lz7bavtco54d2gkj8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2lz7bavtco54d2gkj8w.png" alt="AWS STS" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By switching to OpenID Connect (OIDC), you have successfully eliminated long-lived, high-risk AWS credentials from your Bitbucket environment. Your pipeline now handles deployments using secure, short-lived tokens that rotate automatically with every single run.&lt;/p&gt;

&lt;p&gt;Safe deploying! 🚀&lt;/p&gt;

&lt;p&gt;If you are interested in deploying pipelines like this, you may check out my companion guide: &lt;a href="https://dev.to/melcdn/automate-python-package-releases-semantic-release-aws-codeartifact-poetry-and-bitbucket-pipelines-95m"&gt;Automating Python Package Releases to AWS CodeArtifact via Bitbucket Pipelines&lt;/a&gt;. In that article, we build right on top of this setup to configure automated version bumping, tagging, and secure package distribution.&lt;/p&gt;

&lt;p&gt;Salamat po 🇵🇭&lt;/p&gt;

</description>
      <category>bitbucket</category>
      <category>aws</category>
      <category>oidc</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
