you can add an if key to a job to conditionally run jobs. you also have a lot of metadata available in github actions regarding the event that triggered it and the repo it is on.
because uv supports running scripts with dependencies declared in inline metadata and typer can turn any function into a cli you can put both of them together and build some really powerful small utilities. all you need is to define a function and wrap it in typer.run() in a script with typer as a dependency in the inline metadata.
after some iterations, this is the final script (so far):
issue-to-md.py
# /// script# dependencies = [# "typer",# "rich",# "pyyaml",# ]# ///importjsonimportrefromdatetimeimportdatetimefrompathlibimportPathfromzoneinfoimportZoneInfoimporttyperimportyamlfromrichimportprintfromtyping_extensionsimportAnnotateddefgenerate_post_from_issue(issue_title:Annotated[str,typer.Option("--title","-t")],issue_body:Annotated[str,typer.Option("--body","-b")],issue_labels:Annotated[str,typer.Option("--labels","-l")],issue_created_at:Annotated[str,typer.Option("--created-at","-c")],base_dir:Annotated[str,typer.Option("--base-dir","-d")]="blog/posts",):# Convert labels to a list of tagstags=[label["name"]forlabelinjson.loads(issue_labels)]# Convert ISSUE_CREATED_AT to PST and format as YYYY-MM-DDutc_time=datetime.strptime(issue_created_at,"%Y-%m-%dT%H:%M:%SZ")pst_time=utc_time.astimezone(ZoneInfo("America/Los_Angeles"))created_at_pst=pst_time.date()# Extract the category from the part of the title before the first colon, default to "project" if nonecategory=(issue_title.split(":")[0].strip().lower()if":"inissue_titleelse"project")# Extract the title content after the first colontitle=(issue_title.split(":",1)[1].strip()if":"inissue_titleelseissue_title.strip())# Determine directory based on categorydir_path=Path(base_dir)/("til"ifcategory=="til"else"")dir_path.mkdir(parents=True,exist_ok=True)# Generate a slugified version of the title for the filenameslug=re.sub(r"[^a-z0-9]+","-",title.lower()).strip("-")# Create the front matter dictionaryfront_matter={"title":title,"date":created_at_pst,"categories":[category],"tags":tags,}# Prepare YAML front matter and issue bodyyaml_front_matter=yaml.dump(front_matter,default_flow_style=False)content=f"---\n{yaml_front_matter}---\n\n{issue_body}"# Define filenamefilename=dir_path/f"{slug}.md"# Write content to filefilename.write_text(content,encoding="utf-8")print(f"Markdown file created: {filename}")if__name__=="__main__":typer.run(generate_post_from_issue)
you can automate creating a new markdown file in a directory in your repo with front matter metadata from github issues. you can then create a pull request to deploy those changes to your main branch. my plan is to use this to capture more ideas on the go (on my phone).
.github/workflows/issue-to-md.yml
name:Create Post from Issuepermissions:contents:writepull-requests:writeon:issues:types:[opened]jobs:create-post:runs-on:ubuntu-lateststeps:-name:Checkout repositoryuses:actions/checkout@v4-name:Generate Post from Issueenv:ISSUE_NUMBER:${{ github.event.issue.number }}ISSUE_TITLE:${{ github.event.issue.title }}ISSUE_BODY:${{ github.event.issue.body }}ISSUE_LABELS:${{ toJson(github.event.issue.labels) }}ISSUE_CREATED_AT:${{ github.event.issue.created_at }}run:|# Convert labels to a list of tagsTAGS=$(echo $ISSUE_LABELS | jq -r '.[] | .name' | paste -sd, -)# Convert ISSUE_CREATED_AT to PST and format as YYYY-MM-DDCREATED_AT_PST=$(TZ="America/Los_Angeles" date -d "${ISSUE_CREATED_AT}" +"%Y-%m-%d")# Extract the category from the part of the title before the first colon, default to "project" if noneCATEGORY=$(echo "$ISSUE_TITLE" | awk -F: '{print $1}' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')if [ -z "$CATEGORY" ]; thenCATEGORY="project"fi# Extract the title content after the first colonTITLE=$(echo "$ISSUE_TITLE" | sed 's/^[^:]*: *//')# Determine directory based on categoryif [ "$CATEGORY" = "til" ]; thenDIR="blog/posts/til"elseDIR="blog/posts"fiecho $DIR >> $GITHUB_STEP_SUMMARYecho $CATEGORY >> $GITHUB_STEP_SUMMARY# Generate a slugified version of the title for the filenameSLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//;s/-$//')echo $SLUG >> $GITHUB_STEP_SUMMARY# Create the front matter with category, tags, and formatted dateFRONT_MATTER="---\ntitle: \"$TITLE\"\ndate: ${CREATED_AT_PST}\ncategories: [${CATEGORY}]\ntags: [${TAGS}]\n---"# Prepare content for markdown fileCONTENT="$FRONT_MATTER\n\n$ISSUE_BODY"# Save the content to a markdown fileFILENAME="${DIR}/${SLUG}.md"echo $FILENAME >> $GITHUB_STEP_SUMMARYecho -e "$CONTENT" > "$FILENAME"-name:Commit and push changesenv:ISSUE_TITLE:${{ github.event.issue.title }}ISSUE_NUMBER:${{ github.event.issue.number }}GH_TOKEN:${{ github.token }}run:|git config --local user.name "github-actions[bot]"git config --local user.email "github-actions[bot]@users.noreply.github.com"git checkout -b add-post-$ISSUE_NUMBERgit add .git commit -m "Add post for Issue: $ISSUE_TITLE"git push -u origin add-post-$ISSUE_NUMBERgh pr create --title "#$ISSUE_NUMBER - $ISSUE_TITLE" --body "Adding new post. Closes #$ISSUE_NUMBER"
you can configure your VPS / server to be able to run sudo commands without being asked for your password. you just need to create a sudoers file.
first you have to create sudoers file
sudovisudo-f/etc/sudoers.d/$USER
when i asked chatgpt for this i found you can just run sudo visudo and it’ll open the sudoers file.
now, let’s say you have a user app that you want to be able to run apt update and apt upgrade without asking for sudo password. you need to add this line to your sudoers file