Published: Dec 19, 2024 by Isaac Johnson
I recently noticed long social slugs were still breaking my BlueSky app poster when updating this blog.
In this post we debug then update the Python code to check for size and trim. We also build out helm charts, Github workflows that leverage my on-prem Harbor CR (in a public shared repo). The result is an expose RESTful endpoint anyone can use to post to BlueSky
Checking the errors
I noted that a post still was blocked
<!doctype html>
<html lang=en>
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
I was able to test and replicate locally
$ cat bsky.payload2.json
{"USERNAME": "isaacj.bsky.social", "PASSWORD": "xxxxxxxxxxxxxx", "TEXT": "When one builds a poor man's data center in the basement with discarded and obsolete old tech, there are bound to be failures. Today I wanted to share a few find-and-fix moments maintaining a functioning home lab. From MinIO and NAS Updates to disk remediation. We even have Phantom pages and laptop batteries that are going full marshmallow man (stayin puft).", "LINK": "https://go.tpk.pw/qzpq5"}
$ curl -X POST https://bskyposter.steeped.space/post -H 'Content-Type: application/json' -d @bsky.payload2.json
<!doctype html>
<html lang=en>
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
I went to the cluster to check the logs of the container
$ kubectl logs pybsposter-d9cc5878d-hht85
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://10.42.2.22:5000
Press CTRL+C to quit
10.42.0.20 - - [26/Nov/2024 13:35:35] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:35:35] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:13] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:15] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:16] "GET /server HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:16] "GET /version HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:16] "GET /.vscode/sftp.json HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:17] "GET /about HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:17] "GET /debug/default/view?panel=config HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:17] "GET /v2/_catalog HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:18] "GET /ecp/Current/exporttool/microsoft.exchange.ediscovery.exporttool.application HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:18] "GET /server-status HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:18] "GET /login.action HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:19] "GET /_all_dbs HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:19] "GET /.DS_Store HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:20] "GET /.env HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:20] "GET /.git/config HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:20] "GET /s/034323e2432323e23373e25373/_/;/META-INF/maven/com.atlassian.jira/jira-webapp-dist/pom.properties HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:21] "GET /config.json HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:21] "GET /telescope/requests HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:21] "GET /?rest_route=/wp/v2/users/ HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:36:25] "GET /favicon.ico HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:45] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:45] "GET /favicon.ico HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:47] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:47] "GET /favicon.ico HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:47] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:48] "GET /favicon.ico HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:52] "GET / HTTP/1.1" 404 -
10.42.0.20 - - [26/Nov/2024 13:37:53] "GET /favicon.ico HTTP/1.1" 404 -
[2024-11-26 13:40:59,734] ERROR in app: Exception on /post [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1511, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 917, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
File "/app/app.py", line 20, in handle_post
post = client.send_post(builder)
File "/usr/local/lib/python3.9/site-packages/atproto_client/client/client.py", line 173, in send_post
return self.app.bsky.feed.post.create(repo, record)
File "/usr/local/lib/python3.9/site-packages/atproto_client/namespaces/sync_ns.py", line 788, in create
response = self._client.invoke_procedure(
File "/usr/local/lib/python3.9/site-packages/atproto_client/client/base.py", line 115, in invoke_procedure
return self._invoke(InvokeType.PROCEDURE, url=self._build_url(nsid), params=params, data=data, **kwargs)
File "/usr/local/lib/python3.9/site-packages/atproto_client/client/client.py", line 41, in _invoke
return super()._invoke(invoke_type, **kwargs)
File "/usr/local/lib/python3.9/site-packages/atproto_client/client/base.py", line 122, in _invoke
return self.request.post(**kwargs)
File "/usr/local/lib/python3.9/site-packages/atproto_client/request.py", line 165, in post
return _parse_response(self._send_request('POST', *args, **kwargs))
File "/usr/local/lib/python3.9/site-packages/atproto_client/request.py", line 155, in _send_request
_handle_request_errors(e)
File "/usr/local/lib/python3.9/site-packages/atproto_client/request.py", line 54, in _handle_request_errors
raise exception
File "/usr/local/lib/python3.9/site-packages/atproto_client/request.py", line 153, in _send_request
return _handle_response(response)
File "/usr/local/lib/python3.9/site-packages/atproto_client/request.py", line 79, in _handle_response
raise exceptions.BadRequestError(error_response)
atproto_client.exceptions.BadRequestError: Response(success=False, status_code=400, content=XrpcError(error='InvalidRequest', message='Invalid app.bsky.feed.post record: Record/text must not be longer than 300 graphemes'), headers={'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'cache-control': 'private', 'vary': 'Authorization, Accept-Encoding', 'ratelimit-limit': '5000', 'ratelimit-remaining': '4986', 'ratelimit-reset': '1732628466', 'ratelimit-policy': '5000;w=3600', 'content-type': 'application/json; charset=utf-8', 'content-length': '123', 'etag': 'W/"7b-5OYjOucrkx456vG6nIBiH/VZWIA"', 'date': 'Tue, 26 Nov 2024 13:40:59 GMT', 'keep-alive': 'timeout=90', 'strict-transport-security': 'max-age=63072000'})
We can see that “300” limit is hitting us again
Invalid app.bsky.feed.post record: Record/text must not be longer than 300 graphemes
Just to show that was the cause, I posted a revised entry I manually shortened.
$ cat bsky.payload2.json
{"USERNAME": "isaacj.bsky.social", "PASSWORD": "xxxxxxxxxxxxxx", "TEXT": "When one builds a poor man's data center in the basement with discarded and obsolete old tech, there are bound to be failures. Today I wanted to share a few find-and-fix moments maintaining a functioning home lab. From MinIO and NAS Updates to disk remediation...", "LINK": "https://go.tpk.pw/qzpq5"}
$ curl -X POST https://bskyposter.steeped.space/post -H 'Content-Type: application/json' -d @bsky.payload2.json
{"LINK":"https://go.tpk.pw/qzpq5","TEXT":"<atproto_client.utils.text_builder.TextBuilder object at 0x7f77b87d2a90>","YOU ARE:":"Isaac Johnson"}
which worked
My Github Action should have handled this but didnt
- name: Post to BlueSky
run: |
# clean
rm ./title.txt || true
rm ./bsky.payload.json || true
rm ./target.url || true
rm ./yourls.output.json || true
export LATESTFILE=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9].*markdown\)/\1/'`
export LATESTURL=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-\)\(.*\)\.markdown/\2.html/'`
cat _posts/$LATESTFILE | grep "^title: " | head -n 1 | sed 's/^title: "\(.*\)"/\1/' | tr -d '\n'> title.txt
cat _posts/$LATESTFILE | grep "^social: " | head -n 1 | sed 's/^social: "\(.*\)"/\1/' | tr -d '\n'> social.txt
# I suspect I need an extra space
echo " " | tr -d '\n' >> social.txt
char_count=$(wc -c ./social.txt)
cat _posts/$LATESTFILE | grep "^date: " | head -n 1 | sed "s/^date: .\([0-9]*\)-\([0-9]*\)-\([0-9]*\) .*/https:\/\/freshbrewed.science\/\1\/\2\/\3\/$LATESTURL/g" | sed 's/.markdown/.html/' | tr -d '\n' > target.url
curl --data-urlencode "url=`cat target.url`" --data-urlencode "title=`cat title.txt`" --data-urlencode 'format=json' --data-urlencode 'action=shorturl' --data-urlencode 'signature=6b15f178b9' "https://go.tpk.pw/yourls-api.php" | tee yourls.output.json
cat yourls.output.json | jq -r '.shorturl' | tr -d '\n' > link.txt
echo "================ bluesky ================"
if [ "$char_count" -gt 275 ]; then
echo "{\"USERNAME\": \"$BSKYUSER\", \"PASSWORD\": \"$BSKYPASS\", \"TEXT\": \"`head -c 275 ./social.txt`...\", \"LINK\": \"`cat ./link.txt`\"}" > bsky.payload.json
else
echo "{\"USERNAME\": \"$BSKYUSER\", \"PASSWORD\": \"$BSKYPASS\", \"TEXT\": \"`cat ./social.txt`\", \"LINK\": \"`cat ./link.txt`\"}" > bsky.payload.json
fi
# allow a way to not repost on image updates and hotfixes
log=$(git log -n 1)
set -x
cat bsky.payload.json
if [[ $log != *"SKIPSOCIAL"* ]]; then
echo "================ now posting ================"
curl -X POST https://bskyposter.steeped.space/post -H "Content-Type: application/json" -d @bsky.payload.json
else
echo "CHECK-SKIP: Skip Posting To Social (BSky).. would have posted:"
cat bsky.payload.json
fi
env:
BSKYUSER: $
BSKYPASS: $
GHTOKEN: $
I’m realizing it would be far better to move the logic into the containerized app then try and keep fiddling with my Github actions
Updating the BS Poster App
I’ll clone it locally
builder@DESKTOP-QADGF36:~/Workspaces$ git clone https://github.com/idjohnson/pybsposter.git
Cloning into 'pybsposter'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 9 (delta 0), reused 9 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (9/9), 2.69 KiB | 459.00 KiB/s, done.
builder@DESKTOP-QADGF36:~/Workspaces$ cd pybsposter/
First, I don’t want to have to build the Docker container locally over and over.
Beyond that, Dockerhub’s rate limiting is getting really bad so I want to publish to Github CR or perhaps a public endpoing in my own CR.
Let’s look at the Github Workflow I just created in .github/workflows/container-build.yml
name: PR And Main Build
on:
push:
branches:
- main
pull_request:
jobs:
cicd:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a $ event."
- run: echo "🐧 This job is now running on a $ server hosted by GitHub!"
- run: echo "🔎 The name of your branch is $ and your repository is $."
- name: Check out repository code
uses: actions/checkout@v2
- run: echo "💡 The $ repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: Check on Binaries
run: |
which az
which aws
- name: Build Dockerfile
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
docker build -t $BUILDIMGTAG .
docker images
- id: tagandpush
name: Tag and Push
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
export FINALBUILDTAG="`cat Dockerfile | tail -n1 | sed 's/^#//g'`"
docker tag $BUILDIMGTAG $FINALBUILDTAG
docker images
echo $CR_PAT | docker login harbor.freshbrewed.science -u $CR_USER --password-stdin
docker push $FINALBUILDTAG
env: # Or as an environment variable
CR_PAT: $
CR_USER: $
if: github.ref == 'refs/heads/main'
- id: tagnpushdry
name: Tag and Push (DRY RUN)
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
export FINALBUILDTAG="`cat Dockerfile | tail -n1 | sed 's/^#//g'`"
docker tag $BUILDIMGTAG $FINALBUILDTAG
docker images
echo $CR_PAT | docker login harbor.freshbrewed.science -u $CR_USER --password-stdin
# SHOW THE COMMAND
echo "IF we were on main, we would: docker push $FINALBUILDTAG"
env: # Or as an environment variable
CR_PAT: $
CR_USER: $
if: github.ref != 'refs/heads/main'
- name: Build count
uses: masci/datadog@v1
with:
api-key: $
metrics: |
- type: "count"
name: "dockerbuild.runs.count"
value: 1.0
host: $
tags:
- "project:$"
- "branch:$"
- run: echo "🍏 This job's status is $."
Since I want to use my public CR, I’ll use the existing Forgejo user that has “developer” permissions to push containers there
And I can the set my secrets, namely CR_PAT, CR_USER and DATADOG_API_KEY
Lastly, before I test this flow, it assumes I put the primary destination CR as a comment in the last line of my Dockerfile. I’ll parse the version from here (it allows builds this way)
# Use an official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Define environment variable
ENV NAME=World
# Run app.py when the container launches
CMD ["python", "app.py"]
#harbor.freshbrewed.science/library/pybsposter:0.0.1
Time to see if it works
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git commit -m "Build File"
[main f2bee6c] Build File
2 files changed, 70 insertions(+)
create mode 100644 .github/workflows/container-build.yml
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git push
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 16 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 1.35 KiB | 1.35 MiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/idjohnson/pybsposter.git
9c34f95..f2bee6c main -> main
I can see it fired off a workflow action
I can now see the pull command
docker pull harbor.freshbrewed.science/library/pybsposter@sha256:706b07f0f80917fa35577d3ba80e9053b225e2d4604a1374480aee8eb69aeaee
or
docker pull harbor.freshbrewed.science/library/pybsposter:0.0.1
I’ll test by editting my deployment to use the image
$ kubectl get deployment pybsposter -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"pybsposter","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"pybsposter"}},"template":{"metadata":{"labels":{"app":"pybsposter"}},"spec":{"containers":[{"image":"idjohnson/pybsposter:latest","name":"pybsposter","ports":[{"containerPort":5000}]}]}}}}
creationTimestamp: "2024-11-26T13:31:09Z"
generation: 1
name: pybsposter
namespace: default
resourceVersion: "43494053"
uid: 10985d02-0bbd-43f4-aed7-12ca509a307c
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: pybsposter
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: pybsposter
spec:
containers:
- image: idjohnson/pybsposter:latest
imagePullPolicy: Always
name: pybsposter
ports:
- containerPort: 5000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 1
conditions:
- lastTransitionTime: "2024-11-26T13:31:15Z"
lastUpdateTime: "2024-11-26T13:31:15Z"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: "2024-11-26T13:31:09Z"
lastUpdateTime: "2024-11-26T13:31:15Z"
message: ReplicaSet "pybsposter-d9cc5878d" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 1
replicas: 1
updatedReplicas: 1
$ kubectl edit deployment pybsposter
$ kubectl get deployment pybsposter -o yaml | grep 'image:'
- image: harbor.freshbrewed.science/library/pybsposter:0.0.1
And i can see the rotated pod is using the public image
builder@DESKTOP-QADGF36:~/Workspaces/pybsposter$ kubectl get pods | grep pybs
pybsposter-5f6667dc8c-269rh 1/1 Running 0 60s
builder@DESKTOP-QADGF36:~/Workspaces/pybsposter$ kubectl get pods pybsposter-5f6667dc8c-269rh -o yaml | grep -i image:
- image: harbor.freshbrewed.science/library/pybsposter:0.0.1
image: harbor.freshbrewed.science/library/pybsposter:0.0.1
I used Copilot and Gemini Code Assist and they both suggested the same updated code:
# Calculate the total length of text and link
total_length = len(text) + len(link)
if total_length > 300:
text = text[:(300 - len(link))]
You can see these next changes which include adding a “latest” tag, updated notes in the README.md and deployment.yaml
I can see both tags were pushed
I’ll now update the Github Workflow to build and push a helm chart:
name: PR And Main Build
on:
push:
branches:
- main
pull_request:
jobs:
cicd:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a $ event."
- run: echo "🐧 This job is now running on a $ server hosted by GitHub!"
- run: echo "🔎 The name of your branch is $ and your repository is $."
- name: Check out repository code
uses: actions/checkout@v2
- run: echo "💡 The $ repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: Build Dockerfile
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
docker build -t $BUILDIMGTAG .
docker images
# Package charts
cd charts
helm package pybsposter
- id: tagandpush
name: Tag and Push
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
export FINALBUILDTAG="`cat Dockerfile | tail -n1 | sed 's/^#//g'`"
export FINALBUILDLATEST="`cat Dockerfile | tail -n1 | sed 's/^#//g' | sed 's/:.*/:latest/'`"
docker tag $BUILDIMGTAG $FINALBUILDTAG
docker tag $BUILDIMGTAG $FINALBUILDLATEST
docker images
echo $CR_PAT | docker login harbor.freshbrewed.science -u $CR_USER --password-stdin
docker push $FINALBUILDTAG
# add a "latest" for others to use
docker push $FINALBUILDLATEST
# Push Charts
export CVER="`cat ./charts/pybsposter/Chart.yaml | grep 'version:' | sed 's/version: //' | tr -d '\n'`"
helm push ./charts/pybsposter-$CVER.tgz oci://harbor.freshbrewed.science/library/
env: # Or as an environment variable
CR_PAT: $
CR_USER: $
if: github.ref == 'refs/heads/main'
- id: tagnpushdry
name: Tag and Push (DRY RUN)
run: |
export BUILDIMGTAG="`cat Dockerfile | tail -n1 | sed 's/^.*\///g'`"
export FINALBUILDTAG="`cat Dockerfile | tail -n1 | sed 's/^#//g'`"
docker tag $BUILDIMGTAG $FINALBUILDTAG
docker images
echo $CR_PAT | docker login harbor.freshbrewed.science -u $CR_USER --password-stdin
# SHOW THE COMMAND
echo "IF we were on main, we would: docker push $FINALBUILDTAG"
env: # Or as an environment variable
CR_PAT: $
CR_USER: $
if: github.ref != 'refs/heads/main'
- name: Build count
uses: masci/datadog@v1
with:
api-key: $
metrics: |
- type: "count"
name: "dockerbuild.runs.count"
value: 1.0
host: $
tags:
- "project:$"
- "branch:$"
- run: echo "🍏 This job's status is $."
I won’t list out the charts, but you can see them here
And I can Github Actions workflow worked:
One last change - if I plan to share this, ingress should be disabled by default.
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git diff
diff --git a/charts/pybsposter/Chart.yaml b/charts/pybsposter/Chart.yaml
index 0b13eae..c7a737a 100644
--- a/charts/pybsposter/Chart.yaml
+++ b/charts/pybsposter/Chart.yaml
@@ -2,6 +2,6 @@ apiVersion: v2
name: pybsposter
description: A Helm chart for deploying the pybsposter application
type: application
-version: 0.1.0
+version: 0.1.1
appVersion: "1.0"
diff --git a/charts/pybsposter/values.yaml b/charts/pybsposter/values.yaml
index 604ec99..bdbc378 100644
--- a/charts/pybsposter/values.yaml
+++ b/charts/pybsposter/values.yaml
@@ -8,7 +8,7 @@ service:
targetPort: 5000
type: ClusterIP # Or LoadBalancer
ingress:
- enabled: true # To enable or disable the ingress
+ enabled: false # To enable or disable the ingress
host: bskyposter.steeped.space
tlsSecretName: pybspostergcp-tls
ingressClass: nginx
I can now test by using the OCI URL
$ helm delete pybsposter
$ helm install pybsposter oci://harbor.freshbrewed.science/library/pybsposter --version 0.1.1
Pulled: harbor.freshbrewed.science/library/pybsposter:0.1.1
Digest: sha256:2b02bef80dba8c450c2cb59df7415e93eba439f97f264c6d4e8a28687f6d6796
NAME: pybsposter
LAST DEPLOYED: Fri Dec 6 08:03:53 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Did it make an ingress?
$ kubectl get ingress | grep pybs
$
Now let’s update to enable the ingress
$ helm upgrade pybsposter oci://harbor.freshbrewed.science/library/pybsposter --version 0.1.1 --set ingress.enabled=true
Pulled: harbor.freshbrewed.science/library/pybsposter:0.1.1
Digest: sha256:2b02bef80dba8c450c2cb59df7415e93eba439f97f264c6d4e8a28687f6d6796
Release "pybsposter" has been upgraded. Happy Helming!
NAME: pybsposter
LAST DEPLOYED: Fri Dec 6 08:10:54 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
Testing
Let’s use a longer post. I was a bit wordy just see a content I know would exceed the limit
$ cat /home/builder/Workspaces/jekyll-blog/bsky.payload2.json
{"USERNAME": "isaacj.bsky.social", "PASSWORD": "xxxxxxxxx", "TEXT": "Looking forward to taking the kiddos to the St. Paul Ice Fishing Show today (or this weekend). I keep checking the temps and I hope to get down to the local small lake to test the thickness. I tend to use a pop-up insulated tent but one of these years I hope to get a proper ice-house", "LINK": "https://www.stpaulicefishingshow.com/"}
$ curl -X POST https://bskyposter.steeped.space/post -H 'Content-Type: application/json' -d @/home/builder/Workspaces/jekyll-blog/bsky.payload2.json
{"LINK":"https://www.stpaulicefishingshow.com/","TEXT":"<atproto_client.utils.text_builder.TextBuilder object at 0x7f7b8b0ccb80>","YOU ARE:":"Isaac Johnson"}
That worked, but I wonder if I should add ellipses?
Can I just take a moment to say how much I like Gemini Code Assist? I asked to add the “…” and I planned to account for the extra space by dropping 300 to 297 but I noticed it already accounted for that when it returned the updated Python code
I’ll update for that (and add a space)
uilder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git diff
diff --git a/Dockerfile b/Dockerfile
index c04924b..98340c7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,4 +19,4 @@ ENV NAME=World
# Run app.py when the container launches
CMD ["python", "app.py"]
-#harbor.freshbrewed.science/library/pybsposter:0.0.2
\ No newline at end of file
+#harbor.freshbrewed.science/library/pybsposter:0.0.4
\ No newline at end of file
diff --git a/app.py b/app.py
index 1cf719d..449f4ad 100644
--- a/app.py
+++ b/app.py
@@ -15,8 +15,8 @@ def handle_post():
# Calculate the total length of text and link
total_length = len(text) + len(link)
if total_length > 300:
- text = text[:(300 - len(link))]
-
+ text = text[:(300 - len(link) - 4)] + "... " # Trim and add ellipsis
+
client = Client()
profile = client.login(username, password)
and push
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git add -A
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git commit -m "update app to 0.0.3 and change the code to add an elipse when trimming"
[main e1d78e5] update app to 0.0.3 and change the code to add an elipse when trimming
2 files changed, 3 insertions(+), 3 deletions(-)
builder@DESKTOP-QADGF36:~/Workspaces/pyBSPoster$ git push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 16 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 433 bytes | 433.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
To https://github.com/idjohnson/pybsposter.git
3faa2c1..e1d78e5 main -> main
Since my chart uses the image pull policy of “IfNotPresent” and a tag of “latest”, I’ll want to force it to use a versioned tag to force it to update now
$ helm upgrade pybsposter oci://harbor.freshbrewed.science/library/pybsposter --version 0.1.1 --set ingress.enabled=true --set image.tag=0.0.4
Pulled: harbor.freshbrewed.science/library/pybsposter:0.1.1
Digest: sha256:7d4a74776340b1cdc27c8c757ce0513687ca0865459f65dea67e9edeede9479b
Release "pybsposter" has been upgraded. Happy Helming!
NAME: pybsposter
LAST DEPLOYED: Fri Dec 6 08:18:52 2024
NAMESPACE: default
STATUS: deployed
REVISION: 3
TEST SUITE: None
A delete of the old post and retry
$ curl -X POST https://bskyposter.steeped.space/post -H 'Content-Type: application/json' -d @/home/builder/Workspaces/jekyll-blog/bsky.payload2.json
{"LINK":"https://www.stpaulicefishingshow.com/","TEXT":"<atproto_client.utils.text_builder.TextBuilder object at 0x7fb66b126c10>","YOU ARE:":"Isaac Johnson"}
I’m not going to edit it further, even if it seems odd to trim a status update.
Summary
We updated the BlueSky app to use a Helm Chart and fixed it to automatically trim larger posts. This should prevent our Github Posts from ever dropping due to size issues.
We also changed the Github repo to use Github Actions and push to my self-hosted Harbor CR in a public share (as well as an OCI Helm chart). Thus anyone can now helm install with just this one-liner:
$ helm install pybsposter oci://harbor.freshbrewed.science/library/pybsposter --version 0.1.1
I just overrode some values for my local install
$ helm get values pybsposter
USER-SUPPLIED VALUES:
image:
tag: 0.0.4
ingress:
enabled: true
But all the values
$ helm get values --all pybsposter
COMPUTED VALUES:
image:
pullPolicy: IfNotPresent
repository: harbor.freshbrewed.science/library/pybsposter
tag: 0.0.4
ingress:
certManagerIssuer: gcpleprod2
clientMaxBodySize: "0"
connectTimeout: "3600"
enabled: true
host: bskyposter.steeped.space
ingressClass: nginx
proxyBodySize: "0"
readTimeout: "3600"
sendTimeout: "3600"
sslRedirect: "true"
tlsAcme: "true"
tlsSecretName: pybspostergcp-tls
replicas: 1
service:
port: 80
targetPort: 5000
type: ClusterIP